一、简单diff算法
当新旧vnode的子节点都是一组节点时,为了以最小的性能开销完成更新操作,就要用diff算法。
key的作用:引入key作为vnode的标识,当比较新旧子节点时,如果两个虚拟节点的type属性和key属性都相同,那么就可以进行dom的复用,只要通过打补丁(可能文本子节点内容改变)和dom的移动就能完成更新,比不断执行子节点的卸载和挂载性能要好。
简单diff算法的流程如下:
1、用双重for循环遍历新旧子节点数组,寻找可以复用的dom节点
2、当找到可以复用的dom节点后,要知道这个节点应该移到哪个位置。用一个lastindex变量来存储可以复用的旧子节点索引,如果找到的当前旧子节点索引小于lastindex,说明这个节点对应的真实dom需要往前移动。
3、由于一个虚拟节点被挂载后,对应的真实dom节点会存储在vnode.el中,我们就可以通过旧子节点的vnode.el获取到真实dom,将旧节点的n1.el属性赋值给新节点的n2.el属性,就是真正dom复用的语句。
4、当真实dom需要移动的时候,获取当前新子节点的前一个子节点prevnode,获取prevnode对应的真实dom的下一个兄弟节点作为锚点,用insert方法(依赖原生的insertbefore)插入到锚点元素之前,就能移动到prevnode对应的真实dom的后面了。
5、当有新子节点找不到可以复用的旧子节点,说明这个新子节点是新增的,就将其挂载到prevnode对应的真实dom的后面。
6、当基本的更新结束后,需要遍历旧子节点,然后在新子节点中找具有相同key的节点,若找不到说明要卸载这个节点
二、双端diff算法
vue2使用的就是双端diff算法。他相对于简单diff算法来说,dom移动操作更少,更新性能更优。
双端diff算法的流程如下:
1、创建四个索引值,分别指向新旧两组子节点的头和尾,即oldstartindex、oldendindex、oldendindex、newendindex
2、当oldstartindex<=oldendindex且oldendindex<=newendindex,每一轮循环中进行四步比较,即四个索引对应的节点两两比较,发现可以复用的就移动真实dom节点。
3、若一轮循环中的四步比较都没有找到可以复用的节点,就只能遍历旧的一组子节点,找与新子节点可以复用的节点。
4、在循环结束后要检查索引值,看是否有新节点遗留,或需要卸载的旧节点。
三、快速diff算法
vue3用的就是快速diff算法。
快速diff算法的流程如下:
1、先进行预处理,处理新旧两组子节点中相同的前置节点和相同的后置节点
2、预处理完后剩下的节点用一个source数组来记录新子节点对应在旧子节点中的索引,如果旧子节点中没有那么索引为-1。
3、根据source数组计算出一个最长递增子序列,这个子序列指向的节点就是不需要移动的节点。