DOM Diff原理浅析

353 阅读3分钟

DOM Diff 是通过对比DOM节点与修改后生成的虚拟DOM节点, 并把两个节点的不同之处记录下来,最后根据记录去局部更新页面。

虚拟DOM

我们先来对比一下,创建DOM和创建虚拟DOM有什么不同?

JS创建DOM并添加到页面中

const div = document.createElement('div')	// 创建DOM节点
const span1 = document.createElement('span')	// 创建DOM节点
const span2 = document.createElement('span')	// 创建DOM节点
div.classList.add('red')
div.addEventListener('click',()=>{})
div.appendChild(span1)
div.appendChild(span2)
document.body.appendChlid(div)

Vue创建虚拟DOM并添加到页面中

  • Vue(h函数)
h(			// 创建虚拟DOM节点
  "div",
  {
    class: "red",
    on: {
      click: () => {},
    },
  },
  [h("span", {}, "span1"), h("span", {}, "span2")]
);
new Vue(
	render: h => h(App)
).$mount('#root')

React创建虚拟DOM并添加到页面中

  • React(createElement函数),这里的createElement与JS原生的createElement 不相同
createElement("div"{className:'red',onClick:()=>{}},[
    createElement('span',{},'span1'),
    createElement('span',{},'span2'),
]);
ReactDOM.render(<App/>, document.querySelector('#root'))

使用插件创建虚拟DOM

  • Vue(后缀为vue的文件)
<template>
  <div class="red" @click="()=>{}">
    <span>span1</span>
    <span>span2</span>
  </div>
</template>s

通过vue-loader变成h函数的形式

  • React(后缀为jsx的文件)
function myDom (){
  return (
    <div className="red" onClick={()=>{}}>
      <span>span1</span>
      <span>span2</span>
    </div>
  )
}

通过babel-loader把jsx文件变为createElement

Vue文档中关于虚拟DOM的部分:cn.vuejs.org/v2/guide/re… React文档中关于虚拟DOM的部分:reactjs.org/docs/reconc…

虚拟DOM的优缺点

优点

  • 减少重复的 DOM 操作,保留相同的部分,修改不同的部分
    • 虚拟 DOM 可以将多次操作合并为一次操作,比如你添加 100 个节点,但却是一个接一个操作的(降低频率)
    • 虚拟 DOM 借助 DOM diff 可以把多余的操作省掉,比如你添加 100 个节点,其实只有 8 个是新增的(减少范围)
  • 跨平台
    • 虚拟 DOM 不仅可以变成 DOM,还可以变成小程序、ios 应用、安卓应用、因为虚拟 DOM 本质上只是一个对象

缺点

  • 需要额外的创建函数,如 React的createElement函数 或 Vue的h函数,但可以通过 JSX 或 vue-loader 来简化成 XML 写法

DOM Diff 算法

  • DOM diff 就是一个函数,我们称之为 patch
  • 通过逐层对比元素属性和子元素的不同来进行局部更新,减少重复的 DOM 操作

DOM Diff 可能会带来的问题

  • 需要为每个虚拟DOM节点起个“唯一的名字”,如果“名字”相同,就会出现识别错误的问题,识别错误就会出现更新DOM错误(张冠李戴)
  • Vue 中的 key,React 中的 key就是用来作为虚拟DOM节点的“名字”
  • 任何情况都不要用数组的 index 来作 key(数组中的index会随着数组内元素的修改而改变)

DOM Diff的逻辑

  • Tree diff
    • 将两棵DOM节点树逐层对比,找出哪些节点需要更新
    • 如果节点是组件就进行 Component diff
    • 如果节点是标签就进行 Element diff
  • Component diff
    • 如果节点是组件,就先看组件类型
    • 类型不同直接替换,类型相同则只更新属性
    • 然后进入组件后,对子组件做 Tree diff(递归)
  • Element diff
    • 如果节点是原生标签,则看标签名
    • 标签名不同直接替换,相同则只更新属性
    • 然后进入标签后,对子标签做 Tree diff(递归)

DOM Diff的原理并不复杂,总结下来就是对比不同,局部更新。大家有兴趣可以研究一下DOM Diff算法是如何实现的。

如果你还想了解更多关于虚拟DOM的知识,可以看看司徒正美的这篇文章:www.cnblogs.com/rubylouvre/…