Skip to main content

谈谈虚拟 DOM

是什么

虚拟 DOM 就是虚拟节点。React 用 JS 对象来模拟 DOM 节点,然后将其渲染成真实的 DOM 节点

怎么做

构建虚拟 DOM

用 JSX 语法写出来的 div 其实就是一个虚拟节点:

<div id="x">
<span class="red">hi</span>
</div>

这代码会得到这样一个对象:

{
tag:'div',
props:{
id:'x'
},
children:[
{
tag:'span',
props:{
className:'red'
},
children:[
'hi'
]
}
]
}

能做到这一点是因为 JSX 语法会被转译为 createElement 函数调用(也叫 h 函数),如下:

React.createElement('div',{id:'x'},
React.createElement('span',{class:'red'},'hi')
)

转化为真实 DOM

function render(vdom){
// 1. 特殊DOM节点:如果是字符串或者数字,创建一个文本节点
if(typeof vdom === 'string' || typeof vdom === 'number'){
return document.createTextNode(vdom)
}
// 2. 创建真实DOM
const { tag,props,children } = vdom
const element = document.createElement(tag)
// 3. 设置属性(props)
setProps(element,props)
// 4. 递归遍历子节点,获取真实DOM插入到当前节点
children.map(render).forEach(element.appendChild.bind(element))
// 5. 虚拟DOM 中缓存真是DOM节点(diff算法对比)
vdom.dom = element
// 6. 返回真实 DOM 节点
return element
}

function setProps(){} // 设置属性
function setProp(){} // 略

注意,如果节点发生变化,并不会直接把新虚拟节点渲染到真实节点,而是先经过 diff 算法得到一个 patch 再更新到真实节点上。

解决了什么问题

  1. DOM 操作性能问题:通过虚拟 DOM 和 diff 算法减少不必要的 DOM 操作,保证性能不太差
  2. DOM 操作不方便问题:以前各种 DOM API 要记,现在只有 setState

优点

  1. 为 Reactd 带来了跨平台能力,因为虚拟节点除了渲染为真实节点,还可以渲染为其他东西
  2. 让 DOM 操作的整体性能更好,能通过 diff 减少不必要的 DOM 操作

缺点

  1. 性能要求极高的地方,还是得用真实 DOM 操作

  2. React 为虚拟 DOM 创造了合成事件,跟原生 DOM 事件不太一样

    • 所有 React 事件都绑定到根元素,自动实现事件委托
    • 如果混用合成事件和原生 DOM 事件,有可能会出 bug

如何解决缺点

不用 React,用 Vue3