vue更新dom的diff算法

diff算法使只更新我们修改的那一小块dom而不要更新整个dom: 

在采取diff算法比较新旧节点的时候,比较只会在同层级进行, 不会跨层级比较:

vue更新dom的diff算法

 

 判断是否是相同节点:

function sameVnode (a, b) {
  return (
    a.key === b.key &&  // key值
    a.tag === b.tag &&  // 标签名
    a.isComment === b.isComment &&  // 是否为注释节点
    // 是否都定义了data,data包含一些具体信息,例如onclick , style
    isDef(a.data) === isDef(b.data) &&  
    sameInputType(a, b) // 当标签是<input>的时候,type必须相同
  )
}

如果两个节点是一样的,那么就深入检查他们的子节点。如果两个节点不一样那就可以直接替换oldVnode

function patch (oldVnode, vnode) {
    // some code
    if (sameVnode(oldVnode, vnode)) {
        patchVnode(oldVnode, vnode)   // 相同节点打补丁
    } else {
        const oEl = oldVnode.el // 当前oldVnode对应的真实元素节点
        let parentEle = api.parentNode(oEl)  // 父元素
        createEle(vnode)  // 根据Vnode生成新元素
        if (parentEle !== null) {
            api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl)) // 将新元素添加进父元素
            api.removeChild(parentEle, oldVnode.el)  // 移除以前的旧元素节点
            oldVnode = null
        }
    }
    // some code 
    return vnode
}

当我们确定两个节点相同之后我们会对两个节点执行patchVnode方法:

patchVnode (oldVnode, vnode) {
    const el = vnode.el = oldVnode.el
    let i, oldCh = oldVnode.children, ch = vnode.children
    if (oldVnode === vnode) return
    if (oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text) {
        api.setTextContent(el, vnode.text)
    }else {
        updateEle(el, vnode, oldVnode)
        if (oldCh && ch && oldCh !== ch) {
            updateChildren(el, oldCh, ch)
        }else if (ch){
            createEle(vnode) //create el's children dom
        }else if (oldCh){
            api.removeChildren(el)
        }
    }
}

这个函数做了以下事情:

  • 找到对应的真实dom,称为el
  • 判断VnodeoldVnode是否指向同一个对象,如果是,那么直接return
  • 如果他们都有文本节点并且不相等,那么将el的文本节点设置为Vnode的文本节点。
  • 如果oldVnode有子节点而Vnode没有,则删除el的子节点
  • 如果oldVnode没有子节点而Vnode有,则将Vnode的子节点真实化之后添加到el
  • 如果两者都有子节点,则执行updateChildren函数比较子节点,这一步很重要

updateChildren

不设key,newCh和oldCh只会进行头尾两端的相互比较,设key后,除了头尾两端的比较外,还会从用key生成的对象oldKeyToIdx中查找匹配的节点,所以为节点设置key可以更高效的利用dom。

先进行4种比较:

vue更新dom的diff算法

 

如果是oldS和E相同,那么真实dom中的第一个节点会移到最后;

如果是oldE和S相同,那么真实dom中的最后一个节点会移到最前;

如果oldS和S相同,会调用patchVnode方法,继续判断这两个节点的子节点,oldStartIndex,newStartIndex指向下个节点;

如果oldE和E相同,会调用patchVnode方法,继续判断这两个节点的子节点,oldStartIndex,newStartIndex指向上个节点;

如果以上都不匹配,就尝试在oldChildren中寻找跟newStartVnode具有相同key的节点,如果找不到相同key的节点,说明newStartVnode是一个新节点,就创建一个,然后把newStartVnode设置为下一个节点

我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点 vue更新dom的diff算法

如果以上都不行会依次比较如下:

vue更新dom的diff算法

 

即把C更新成F,D更新成C,E更新成D,最后再插入E 

 

原文:https://www.cnblogs.com/wind-lanyan/p/9061684.html

上一篇:Vue原理-diff比对算法


下一篇:linux-使用目录遍历攻击执行命令