虚拟 DOM 比真 DOM 烂

前言

在开始之前,说明一下为什么写这篇文章吧。虚拟 DOM(本文后面统一 vDOM),这个词经常在面试、社区文章中出现,都在说 vDOM 快、至于快在哪里也没个人能说的清楚(当然,我也不一定是对的,主要是分享自己对 vDOM 和 DOM 的看法),从网上看到的资料我能了解到的就是使用 JavaScript 的普通对象抽象出一个 vDOM 树,当数据变化的时候重新生成 vDOM 树,再通过 diif 算法比较 vDOM 和的 vDOM 对比,看看是哪块的数据变动了,最后将变动的地方映射到 DOM 上,从而完成页面数据的展示。那么问题就来了,既然最后都要修改 DOM 为什么还要弄出一个 vDOM 呢?吃饱了没事干?还遍历对比新旧 vDOM 的差异,饶了一圈不还是要改 DOM 吗?我陷入了沉思…..

正文

什么是 vDOM 我就不多说了,基于前言最后一句话的思考,我更想了解 vDOM 它究竟带来什么优点,前端巨头 Vue、React 都在用,这究竟是为什么??

于是我打开了 Vue 的官网 Virtual DOM,并未在文档长看到有任何地方描述 vDOM 效率高、速度快、内存占用低。

再来看看 React 官网 Virtual DOM - 中文 Virtual DOM - 英语,和 Vue 一样,并未提起 vDOM 效率高、速度快、内存占用低等字眼。其中有一句话:在 React 的世界里,术语 “Virtual DOM” 通常与 React 元素关联在一起,因为它们都是代表了用户界面的对象

因此 vDOM 并不是在提高效率,而是在消耗效率。为什么这么说?因为同样是前端框架,后起之秀 Svelte.jsSolid.js 都没有使用 vDOM 反而效率还吊打 Vue.js 和 React.js ,效率甚至直逼 Vanilla.js (原生 JS)

为什么需要虚拟 DOM

那么问题来了,为什么需要 vDOM,vDOM 效率不是比 DOM 低吗?为什么还要用?整那么多判断和数据对比,最后不还是要改 DOM 吗?为什么不直接改 DOM?

我们先想想 vDOM 究竟带来了什么优势。

  1. 跨平台:借助一些工具可以将 vDOM 转换成对应平台可以认识的东西
    例如在浏览器中 react 就需要 react-dom 这个库在实现将 vDOM 转换成真实 DOM。
    还有 react-native 将 vDOM 转换成 AndroidiOS 平台可以使用的用户界面。

    再举个 🌰,国内 uniApp 很多前端开发多多少少都听过吧?做小程序,或 APP 的,它使用 Vue vDOM 转换成对应平台、iOS、Android、小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)

  2. 简单方便: 能让你轻松的操作 DOM,也不用你去维护 DOM 的状态。
    不像浏览器中的 DOM 每次都要 document.querySelector,甚至还要判断获取的这个真实 DOM 到底存不存在,这样写代码就非常的恶心

  3. 性能提升: 不是说 vDOM 性能比不上 DOM 吗?为什么还说它性能提升?
    在某些情况下,vDOM 性能确实是要比 DOM 性能好,如果有 1000 个 DOM 节点(举例 ul>li),这是后端返回的数据,需要渲染到页面上(一般人谁没事后端返回你那么多数据干嘛,更何况你也不能一次性渲染那么多数据啊,用户又看不到那么多,基本上都是做分页处理),假如有某种业务需求,修改了 100 个 li 接下来看看 vDOM 和 DOM 是怎么运行的吧

    vDOM 生成一个新的 vDOM 跟旧的 vDOM 对比,发现有 100 个 li 数据发生了变化,这时 vDOM 开始修改 DOM ,这个过程中只触发一次 DOM 修改

    DOM 可能也会和 vDOM 一样最后只触发一次 DOM 修改,也可能触发 100 次左右的 DOM 修改,具体看代码是怎么实现

COPY
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
!(() => {
const ul = document.querySelector('ul')
for (let i = 0; i < 100; i++) {
const li = document.createElement('li')
li.textContent = i
// 关键点
// 每次给 ul 的子元素都添加一个 li,就排版一次,100次循环结束后排版了100次,也绘制渲染了100次
// 不知道我这样说是不是对的,因为我对浏览器渲染原理也不是很了解。重排、重绘
ul.appendChild(li)
}
})()

!(() => {
const ul = document.querySelector('ul')
const fragment = document.createDocumentFragment()
for (let i = 0; i < 100; i++) {
const li = document.createElement('li')
li.textContent = i
// 关键点
// 每次给文档碎片里添加一个 li 这只是在内存中,并没有添加到页面上,所以页面并会不会发生什么变化
fragment.appendChild(li)
}
// 直到循环完毕后,将文档碎片里的所有 li 一次性添加到 ul 里,此时页面只触发一次重排、重绘
ul.appendChild(fragment)
})()

总结

本文仅仅是乐特对 vDOM 的理解和感悟,并未真正的去翻看文中提到的框架源码,所以我希望大家看完这篇文章后能够有一些自己的看法和理解,并自己行动去寻找自己的答案,而不是在网上看到别人写的文章就真的以为别人说的就是对的,从而丧失自己的判断里。就像网上发的短视频,未知事情真伪,仅仅是看到了别人裁剪视频的一小部分,就在评论区跟风

总结(这才是真正的总结),vDOM 并没有提高效率,反而是在降低效率,继续上面所说的 1000 个 DOM 节点,如果我只修改了 1 个 li 呢?vDOM 还是会生成一个新的 vDOM 和旧的 vDOM 对比,白白跑了 1000 次

如果有哪写的不对的,还请各位大佬指出 😁😁

Authorship: Lete乐特
Article Link: https://blog.imlete.cn/article/Virtual-DOM-slower-real-DOM.html
Copyright: All posts on this blog are licensed under the CC BY-NC-SA 4.0 license unless otherwise stated. Please cite Lete乐特 's Blog !