虚拟 DOM 的原理是什么?
什么是虚拟DOM?
虚拟 DOM 就是虚拟节点。React 用 JS 对象来模拟 真实DOM 节点,然后将其渲染成真实的 DOM 节点。
如何做到?
第一步
用 JSX 语法写出来的 div 其实就是一个虚拟节点。例如以下代码:
<div id="x">
<span class="red">hi</span>
</div>
JSX 语法会被转译为 createElement 函数调用(也叫 h 函数)。
React.createElement("div", { id: "x"},
React.createElement("span", { class: "red" }, "hi")
)
会得到类似于这样一个对象:
{
tag: 'div',
props: {
id: 'x'
},
children: [
{
tag: 'span',
props: {
className: 'red'
},
children: [
'hi'
]
}
]
}
第二步
将虚拟节点渲染为真实节点。
如果节点发生变化,并不会直接把新虚拟节点渲染到真实节点,而是先经过 diff 算法得到一个 patch 再更新到真实节点上。
解决了什么问题
- DOM 操作性能问题。通过虚拟 DOM 和 diff 算法减少不必要的 DOM 操作,保证性能不太差。
- DOM 操作不方便问题。以前各种 DOM API 要记,现在只有 setState
优点
- 为 React 带来了跨平台能力,因为虚拟节点除了渲染为真实节点,还可以渲染为其他东西。
- 让 DOM 操作的整体性能更好,能(通过 diff)减少不必要的 DOM 操作。
缺点
- 性能要求极高的地方,还是得用真实 DOM 操作(目前没遇到这种需求)
-
React 为虚拟 DOM 创造了合成事件,跟原生 DOM 事件不太一样,工作中要额外注意
- 所有 React 事件都绑定到根元素,自动实现事件委托
- 如果混用合成事件和原生 DOM 事件,有可能会出 bug
React/Vue 的 DOM diff 算法是怎样的?
什么是DOM diff
DOM diff 就是对比两棵虚拟 DOM 树的算法(废话很重要)。当组件变化时,会 render 出一个新的虚拟 DOM,diff 算法对比新旧虚拟 DOM 之后,得到一个 patch,然后 React 用 patch 来更新真实 DOM。
怎么做的
-
首先对比两棵树的根节点
- 如果根节点的类型改变了,比如 div 变成了 p,那么直接认为整棵树都变了,不再对比子节点。此时直接删除对应的真实 DOM 树,创建新的真实 DOM 树。
-
如果根节点的类型没变,就看看属性变了没有
- 如果没变,就保留对应的真实节点。
-
如果变了,就只更新该节点的属性,不重新创建节点。
- 更新 style 时,如果多个 css 属性只有一个改变了,那么 React 只更新改变的。
-
然后同时遍历两棵树的子节点,每个节点的对比过程同上。
- 情况一
<ul> <li>A</li> <li>B</li> </ul> <ul> <li>A</li> <li>B</li> <li>C</li> </ul>React 依次对比 A-A、B-B、空-C,发现 C 是新增的,最终会创建真实 C 节点插入页面。
- 情况二
<ul> <li>B</li> <li>C</li> </ul> <ul> <li>A</li> <li>B</li> <li>C</li> </ul>React 对比 B-A,会删除 B 文本新建 A 文本;对比 C-B,会删除 C 文本,新建 B 文本;(注意,并不是边对比边删除新建,而是把操作汇总到 patch 里再进行 DOM 操作。)对比空-C,会新建 C 文本。
为什么 React 做不到呢?
因为 React 需要你加 key 才能做到:
<ul>
<li key="b">B</li>
<li key="c">C</li>
</ul>
<ul>
<li key="a">A</li>
<li key="b">B</li>
<li key="c">C</li>
</ul>
React 先对比 key 发现 key 只新增了一个,于是保留 b 和 c,新建 a。
React DOM diff 和 Vue DOM diff 的区别?
- React 是从左向右遍历对比,Vue 是双端交叉对比(头头对比,尾尾对比,旧头新尾,旧尾新头,再对比Key)。
- React 需要维护三个变量,Vue 则需要维护四个变量。
-
Vue 整体效率比 React 更高,举例说明:假设有 N 个子节点,我们只是把最后子节点移到第一个,那么
- React 需要进行借助 Map 进行 key 搜索找到匹配项,然后复用节点
- Vue 会发现移动,直接复用节点