虚拟DOM和DOM diff

98 阅读2分钟

虚拟DOM

是什么

一个能代表DOM树的对象,通常含有标签名,标签上的属性,事件监听和子元素们,以及其他属性。

优点(相比于操作原生DOM只在以下情况下快)

减少不必要的DOM操作

可以通过合并DOM操作来减少DOM操作的次数;

也可以通过DOM diff算法来减少需要改变的个数,缩小范围。

跨平台

虚拟DOM本质上是一个JS对象,它不仅可以变成DOM对象,而且可以变成小程序、IOS应用、安卓应用等。

//React 中的虚拟DOM
const vNode = {
  key:null,
  props:{
    children:[
      {type:'span','span1'},
      {tyoe:'span','span2'}
    ],
    className:'red'
    onclick:()=>{}
  },
  ref:null,
  type:'div',
  ...
}
//创建虚拟DOM
createElement('div',{className:'red',onClick:()=>{}},[
  createElement('span',{},'span1'),
  createElement('span',{},'span2'),
  ]
)
//Vue中的虚拟DOM
const vNode = {
  tag:'div',
  data:{
    class:'red',
    on:{
      click:()=>{}
    }
  },
  children:[
    {tag:'span','span1'},
    {tag:'span','span2'}
  ],
  ...
}
//创建虚拟DOM
h('div',{
  class:'red',
  on:{
    click:()=>{}
  },
},[h('span',{},'span1'),h('span',{},'span2')])

React使用JSX语法来简化创建虚拟DOM,通过babel转为createElement形式。

<div className='red' onClick='{()=>{}}'>
  <span>span1</span>
  <span>span2</span>
</div>

Vue使用Vue Template来简化创建虚拟DOM,通过vue-loader转为h形式。

<div class='red' @click='fn'>
   <span>span1</span>
  <span>span2</span>
</div>

缺点

必须使用babelvue-loader来将JSX语法/vue-template转化为创建函数createElement或者h

DOM diff

当数据变化时,DOM diff算法会只更新变化的地方,如标签的属性从class='red'变为class='green'时,只需更新标签的属性。其他均不更新。

一个函数,patchpatches=patch(oldNode,newNode)

大致的作用原理:Tree diff,将新旧DOM树逐层对比,找出需要更新的节点。如果节点是组件,就看Component diff;如果节点是标签,就看Element diff

Component diff:如果节点是组件,就先看组件类型,类型不同直接替换,类型相同就只更新属性。然后继续深入组件做Tree diff

Element diff:如果节点是原生标签,则看标签名。标签名不同直接替换,相同则只更新属性。然后进入标签后作Tree diff

缺点

同级比较时存在bug,需要使用key值来确定需要更新的节点。