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/…