一、什么是虚拟DOM
虚拟DOM就是虚拟节点。React用JS对象来模拟DOM节点,然后将其渲染成真实的DOM节点,因此虚拟DOM的本质是JS对象。
虚拟 DOM 就是用来模拟 DOM 的一个对象,这个对象拥有一些重要属性,并且更新 UI 主要就是通过对比(diff)旧的虚拟 DOM 树 和新的虚拟 DOM 树的区别完成的。
二、如何实现虚拟DOM
第一步:模拟
用JSX 语法写出来的div其实就是一个虚拟节点:
<div id="x">
<span class="red">hi</span>
</div>
这段代码会得到这样一个对象,这个对象有一些重要的属性tag,props,children:
{
tag: 'div',
props: {
id: 'x'
},
children: [
{
tag: 'span',
props: {
className: 'red'
},
children: [
'hi'
]
}
]
}
以上代码的转换是因为,JSX 语法会被转译为React.createElement函数调用(也叫 h 函数),如下:
React.createElement("div", { id: "x"},
React.createElement("span", { class: "red" }, "hi")
)
第二步:将虚拟节点渲染为真实节点
其过程就是根据JS对象已有的重要属性(tag,props,children)去依次创建真实的DOM并返回
function render(vdom) { //传入虚拟DOM节点
if (typeof vdom === 'string' || typeof vdom === 'number') {
return document.createTextNode(vdom)
}
const { tag, props, children } = vdom
const element = document.createElement(tag)
setProps(element, props)
children
.map(render)
.forEach(element.appendChild.bind(element))
vdom.dom = element
return element // 返回 真实的DOM 节点
}
节点更新
如果节点发生变化,并不会直接把新的虚拟节点渲染到真实节点,而是先经过 diff 算法得到一个 patch ,再更新到真实节点上,详细内容请查看我的另一篇DOM diff 算法。
三、虚拟DOM解决了什么问题
- DOM 操作性能问题。通过虚拟 DOM 和 diff 算法减少不必要的 DOM 操作,DOM 操作较慢,频繁变动 DOM 会造成浏览器的回流,因此我们需要 diff 后中尽可能一次性将差异更新到真实的 DOM 上。
- DOM 操作不方便问题。DOM API 统一换成使用 setState
四、优点
- 为 React 带来了跨平台能力,因为虚拟节点除了渲染为真实节点,还可以渲染为其他东西。
- 让 DOM 操作的整体性能更好,能通过 diff 减少不必要的 DOM 操作。
五、缺点
- 性能要求极高的地方,还是得用真实 DOM 操作。
- React 为虚拟 DOM 创造了合成事件,跟原生 DOM 事件不太一样,需要额外注意(比如 React 自动实现了事件委托,对元素的操作都绑定到根元素上)