Vue中的diff算法

diff算法是什么

diff算法是一种通过同层的树节点进行比较的高效算法

其有两个特点:

  • 比较只会在同层级进行,不会跨层级比较
  • 在dff比较的过程中,循环从两边向中间比较(首位交叉对比

diff算法在很多场景下都有应用,在Vue中,作用于虚拟dom渲染成真实dom的新旧VNode节点比较

比较方式

diff整体策略为:深度优先,同层比较

  1. 比较只会在同层级进行,不会跨层级比较
    在这里插入图片描述
  2. 比较的过程中,循环从两边向中间收拢
    在这里插入图片描述
    下面举个vue通过diff算法更新的例子:
    新旧VNode节点如下图所示:
    在这里插入图片描述
    第一次循环后,发现旧节点D与新节点D相同,直接复用引旧节点D作为diff后的第一个真实节点,同时旧节点endIndex移动到C,新节点的startIndex移动到了C
    在这里插入图片描述
    第二次循环后,同样是旧节点的末尾和新节点的开头(都是C)相同,同理,diff后创建了C的真实节点插入到第一次创建的D节点后面。同时旧节点的endIndex移动到了B,新节点的startIndex移动到了E
    在这里插入图片描述
    第三次循环中,发现E没有找到,这时候只能直接创建新的真实节点E,插入到第二次创建的C节点之后。同时新节点的startIndex移动到了A。旧节点的startIndex和endIndex都保持不动。
    在这里插入图片描述

源码分析

当数据发生改变时,set方法会调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图
源码位置:src/core/vdom/patch.js

patch

function patch(oldVnode, vnode, hydrating, removeOnly) {
   
    if (isUndef(vnode)) {
    // destory
        if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
        return
    }

    let isInitialPatch = false
    const insertedVnodeQueue = []

    if (isUndef(oldVnode)) {
   
        isInitialPatch = true
        createElm(vnode, insertedVnodeQueue) // do
        m
    } else {
   
        const isRealElement = isDef(oldVnode.nodeType)
        if (!isRealElement && sameVnode(oldVnode, vnode)) {
   
            // patchVnode
            patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
        } else {
   
            // dom
            if (isRealElement) {
   

                if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {
   
                    oldVnode.removeAttribute(SSR_ATTR)
                    hydrating = true
                }
                if (isTrue(hydrating)) {
   
                    if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
   
                        invokeInsertHook(vnode, insertedVnodeQueue, true)
                        return oldVnode
                    }
                }
                oldVnode = emptyNodeAt(oldVnode)
            }
            return vnode.elm
        }
    }
}

patch函数前两个参数位为oldVnodeVnode,分别代表新的节点和之前的旧节点,主要做了四个判断:

  • 没有新节点,直接触发旧节点的destory钩子
  • 没有旧节点,说明是页面刚开始初始化的时候,此时,根本不需要比较了,直接全是新建,所以只调用createElm
  • 旧节点和新节点自身一样,通过sameVnode判断节点是否一样,一样时,直接调用patchVnode去处理这两个节点
  • 旧节点和新节点自身不一样,当两个节点不一样的时候,直接创建新节点,删除旧节点

patchVnode

function patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly) {
   
    // 如果新旧节点一致,什么都不做
    if (oldVnode === vnode) {
   
        return
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值