虚拟DOM原理

334 阅读2分钟

一、什么是虚拟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 自动实现了事件委托,对元素的操作都绑定到根元素上)