Vue diff算法
什么是diff算法
diff算法就是比较新旧DOM树,寻找差异的算法,在源码中是通过patch函数来完成的,所以我们也称作为patch算法 diff算法比较策略:深度优先,同层比较
深度优先:在深度优先搜索中,会先访问根节点,然后一直沿着某一分支搜索到底,直到不能再沿该分支往下走为止,如果查找的过程中发现下面的子节点为空。退回到上一节点,遍历该节点的另一分支,深度优先搜索可以使用递归或者栈来实现。
同层比较:相同深度的虚拟dom树中的节点进行比较,也就是同一层的Dom节点进行比较
diff算法执行过程
1.新旧虚拟dom的保存
- 当组件内部的响应式数据发生更新的时候,就会执行vue内部的updateComponent函数,在函数内部先执行_render方法生成新的虚拟DOM,把虚拟DOM传递给_update方法,并调用_update方法
- 在_update函数中,首先定义一个变量保存旧的虚拟DOM(在vm._vnode属性上),然后再把_update接受的新的虚拟DOM vNode放在vm的_vnode属性上,此时在_update函数中就有了新旧虚拟DOM,最后使用patch方法对新旧虚拟DOM进行比较 伪代码:
function Vue() {
//生成新的虚拟dom
const updateComponent = () => {
this._update(this._render());
};
}
function _update(vNode) {
//因为虚拟DOM都会在实例的_vnode属性上,把新的虚拟DOM放在vm实例上,所以要先使用一个变量保存住旧的虚拟DOM用来diff比较
//c创建一个变量保存旧的虚拟dom,如果保存,实例上的旧的虚拟dom就会被覆盖
const oldVnode = vm._vnode;
vm._vnode = vNode;
// 使用pacth函数新旧虚拟DOM的比较
patch(oldVnode, vNode);
}
2.patch的比较过程
比较父节点
- patch函数首先使用sameVnode方法专门比较两个节点是否相同(相同:两个虚拟DOM节点的标签类型相同[input标签还要比较type类型],并且key的值也要相同,如果没有写key,则key的值是undefined), 如果sameVnode比较两个节点相同,则直接进入更新流程(1.把旧节点的真实DOM拿到新节点位置复用 2.对比新旧节点的属性是否相同,如果不同则更新 3.开始比较子节点), 如果sameVnode比较两个不相同,则直接遍历新的节点创建新的元素,并直接删除旧的元素
两个虚拟dom的比较过程,是先比较出两个虚拟dom的区别,记录差异,最后再修改真实DOM
将旧节点的真实 DOM 元素复用到新节点所在位置的过程中,并不会改变这些元素本身的内容和属性,也不会添加新的真实 DOM 元素。在这个过程中实际上只是把原本已经存在于页面中的某些元素进行了移动和重排,而不是对它们进行重新创建或更新。因此可以理解为原来的真实 DOM 元素本身没有发生变化,只是它们的位置被移动了
根节点一般是比较特殊的,不需要写key,vm实例中不配置template的时候,他就是外层的容器
直接遍历新的节点创建新的元素:意思是遍历新虚拟dom整个节点以及它的子节点,创建一个全新的真实DOM
比较子节点(比较子节点是否相等也是使用的sameVnode)
- vue使用四个指针分别指向 新旧子节点列表 的 首尾节点 首先比较新旧树的头指针,判断是否相同,如果相同则进入更新流程..... 继续比较两个树的头指针,如果不相同,则比较新旧树的尾指针,如果相同则进入更新流程.... 如果头指针比较和尾指针比较都不相同,则比较头尾指针是否相同,如果相同则进入更新流程... 如果上边的规则都不相等,则会以新树的头指针为基础,循环旧的虚拟DOM节点,如果存在相等则直接拿过进入更新流程,如果找不到则直接拿当前的新的虚拟DOM节点创建真实DOM 当新树的头指针超过尾指针的时候,比较结束,如果旧树中存在剩余节点,则删除这个剩余节点
比较概括:头头,尾尾,头尾,尾头,遍历 在执行算法的过程的时候,是tag标签和key一块进行比较,必须都要相同
key的作用:
- 在新旧虚拟DOM对比更新的时候,默认的diff算法是"就地复用"原则
- "就地复用":多个子节点比较的时候,如果没有添加key属性,则key属性都是undefined,所以每一个新旧DOM的key都是相同的,所以就会简单的按照节点的顺序依次比较(如果新旧节点的顺序发生变化,vue仍然都是创建新节点删除旧节点)
- 我们可以给每一个节点添加一个key属性,方便Vue跟踪每一个元素的身份,从而在diff算法计算的时候可以按照key确定比较节点,这样可以更高效的更新渲染虚拟DOM
- 不要使用遍历出来的index作为key(index不是稳定性),key的要求是 唯一性!!! 稳定性!!!