虚拟DOM与DOM diff

1,091 阅读5分钟

一、什么是虚拟DOM?

> 1.DOM操作快不快?

DOM操作比任何第三方库都快,它只是比原生JS慢。

> 2.虚拟DOM有什么优点?

A 减少DOM操作

  • 减少DOM操作的次数
    • 合并DOM操作,例如要添加1000个节点,一个接一个操作肯定麻烦
  • 减少DOM操作的范围
    • 借助DOM diff将多余的操作省掉,例如添加1000个节点,其实只有十个是新增的

B 跨平台

  • 因为虚拟DOM本质上只是一个JS对象
    • 所以虚拟DOM不仅可以变成一个DOM,
    • 还可以变成小程序,ios应用,安卓应用……

> 3.虚拟DOM长什么样子?

统一来说,看这个虚拟DOM表示什么DOM,子节点怎么看,事件还有标签上的属性怎么表示就知道它的结构了。

A React中的虚拟DOM结构

image.png

const VNode = {
    key:null,
    props:{
        children:[ //子节点
        {type:'span',……},
        {type:'span',……},
        ],
        className:"red", //标签上的属性
        onClick:()=>{}  //事件
    },
    ref:null,
    type:'div'  //标签名or属性名
    ……
}
B Vue中的虚拟DOM结构

渲染机制 | Vue.js (vuejs.org)

const vnode = { 
    type: 'div', 
    props: { id: 'hello' }, 
    children: [ /* 更多 vnode */ ] 
}

> 4.如何得到虚拟DOM?

A React
  • React.createElement

React 顶层 API – React (reactjs.org)

//React中的.js文件
createElement('div', {className='red' , onClick:()=>{}} , [
    createElement('span',{},'span1'),
    createElement('span',{},'span2')
])
  • JSX写法
    • 通过Babel转换为createElement
//React中的.jsx文件
<div className='red' onClick='{()=>{}}'>
    <span>span1</span>
    <span>span2</span>
</div>
B Vue
  • render函数中的h()

渲染函数 API | Vue.js (vuejs.org)

//.vue文件
h('div',{
        class:'red',
        on:{click:()=>{}}
    },
    [h('span',{},'span1'),h('span',{},'span2')] 
)
  • JSX写法
    • 通过vue-loader转换为h
// Vue Template  .vue文件的template标签中
<div class='red' @click='fn'>
    <span>span1</span>
    <span>span2</span>
</div>

> 5.关于虚拟DOM的总结

Ⅰ 虚拟DOM代表什么?

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

Ⅱ 虚拟DOM有什么优点?

能减少不必要的DOM操作;能跨平台渲染

Ⅲ 虚拟DOM有什么缺点?

需要额外的创建函数,例如createElement和h,但是可以通过TSX来简化成XML的写法

二、真实DOM和虚拟DOM谁更快?

当规模小的时候,虚拟DOM是非常快的;当规模非常大的时候,虚拟DOM的额外计算会消耗大量时间,而此时的真实DOM是非常快的,并且能够保持稳定性不会崩溃。

> 如何证明?

  • 在JS文件中创建1000个节点,打印出运行时间
  • 在React的JS文件中创建1000个节点,打印出运行时间
  • 然后10倍扩大需要创建的节点数量

三、DOM diff : 虚拟DOM的对比算法

> 1.什么是DOM diff?

  • Dom diff 就是一个函数,称为patch
  • patch函数做的事情就是对比新旧dom得到不同的地方:patches = patch(oldVNode,newVNode)
  • patches就是我们要运行的DOM操作

举两个DOM diff工作的例子,一个体现优点,一个展示缺点: 原始的虚拟DOM

Dom diff简化DOM操作 DOM diff的缺点

> 2.DOM diff的运行逻辑是怎么样的呢?

它可能的大概逻辑是这样的。

  • Tree diff

    • 将新旧两棵树逐层对比,找出哪些节点需要更新

      • 如果结点是组件就看Component diff

      • 如果结点是标签就看Element diff

  • Component diff

    • 如果节点是组件,就先看组件类型

      • 类型不同直接替换(删除旧的)

      • 类型相同则只更新属性

    • 然后深入组件做Tree diff(递归)

  • Element diff

    • 如果节点是原生标签,则看标签名

      • 标签名不同直接替换

      • 相同则只更新属性

    • 然后进入标签后代做Tree diff(递归)

> 3.DOM diff的缺点

  • DOM diff的优点一早就说了,也就是可以减少DOM操作的范围,减少不必要的DOM操作

  • DOM diff的bug也在上文展示了,当你的节点没有key的时候,同级比较会产生误会

    • 这也就是为什么v-for中需要:key
    • 计算机做的事情就是,不管你删除哪一个,只要是一个同级同类型的标签,计算机会直接删除最后一个节点。
  • 举个例子: Vue2.0 v-for 中 :key 到底有什么用?

> 4.关于DOM diff的深入阅读

四、最后做一个重点内容的总结

> 1.虚拟 DOM 是什么

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

> 2.虚拟 DOM 的优点

A 减少DOM操作

  • 减少DOM操作的次数
    • 合并DOM操作,例如要添加1000个节点,一个接一个操作肯定麻烦
  • 减少DOM操作的范围
    • 借助DOM diff将多余的操作省掉,例如添加1000个节点,其实只有十个是新增的

B 跨平台

  • 因为虚拟DOM本质上只是一个JS对象
    • 所以虚拟DOM不仅可以变成一个DOM,
    • 还可以变成小程序,ios应用,安卓应用……

> 3.虚拟 DOM 的缺点

需要额外的创建函数,例如createElement和h,但是可以通过TSX来简化成XML的写法

> 4.DOM diff 是什么

  • Dom diff 就是一个函数,称为patch
  • patch函数做的事情就是对比新旧dom得到不同的地方:patches = patch(oldVNode,newVNode)
  • patches就是我们要运行的DOM操作

> 5.DOM diff 的优点

也就是可以减少DOM操作的范围,减少不必要的DOM操作

> 6.DOM diff 的问题(key)

  • DOM diff的优点一早就说了,也就是可以减少DOM操作的范围,减少不必要的DOM操作

  • DOM diff的bug也在上文展示了,当你的节点没有key的时候,同级比较会产生误会

    • 这也就是为什么v-for中需要:key
    • 计算机做的事情就是,不管你删除哪一个,只要是一个同级同类型的标签,计算机会直接删除最后一个节点。
  • 举个例子: Vue2.0 v-for 中 :key 到底有什么用?