最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 浅谈vue中key的作用及工作原理

    正文概述 掘金(倔强的小马同学)   2021-01-31   703

    源码中找答案:corn/vdom/patch.js (updateChildren方法)

    demo:

    const app = new vue({
        el:'#demo',
        data: {item:['a','b','c','d',]},
        mounted(){
            setTimeout(() => {
                this.item.splice(2,0,'E')
            })
        }
    })
    

    执行以上代码,正常返回的结果应该是:a,b,e,c,d ,e插入到b和c中间;但是在不适用key的情况回发生什么呢?

    由于没有key,程序识别不出来到底要更新谁。所以就只能做一个操作,那就是见着谁就更新谁,这样就会形成一种错误,不改更新的被更新了,需要更新的却没有准确的被更新。

    浅谈vue中key的作用及工作原理

    那么再来看下有key的情况

    // 第一次循环  patch A
       A B C D
       A B E C D 
    
    // 第二次循环 patch B
        B C D
        B E C D
    
    // 第三次循环    根据首尾判断策略  这次patch D
        C D
        E C D
    
    // 第四次循环   patch C
       C
       E C
    // 最后一次新数组只剩下E了,老数组中已经全部patch结束,创建F,插入到C前面
    

    我们现在来看下源码的解释

    function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {    let oldStartIdx = 0    let newStartIdx = 0    let oldEndIdx = oldCh.length - 1    let oldStartVnode = oldCh[0]    let oldEndVnode = oldCh[oldEndIdx]    let newEndIdx = newCh.length - 1    let newStartVnode = newCh[0]    let newEndVnode = newCh[newEndIdx]    let oldKeyToIdx, idxInOld, vnodeToMove, refElm    // removeOnly is a special flag used only by <transition-group>    // to ensure removed elements stay in correct relative positions    // during leaving transitions    const canMove = !removeOnly    if (process.env.NODE_ENV !== 'production') {      checkDuplicateKeys(newCh)    }    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {      if (isUndef(oldStartVnode)) {        oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left      } else if (isUndef(oldEndVnode)) {        oldEndVnode = oldCh[--oldEndIdx]      } else if (sameVnode(oldStartVnode, newStartVnode)) { // 老的开头节点与新的开头节点比较        patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)        oldStartVnode = oldCh[++oldStartIdx]        newStartVnode = newCh[++newStartIdx]      } else if (sameVnode(oldEndVnode, newEndVnode)) { // 老的末尾节点与新的末尾节点作比较        patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)        oldEndVnode = oldCh[--oldEndIdx]        newEndVnode = newCh[--newEndIdx]      } else if (sameVnode(oldStartVnode, newEndVnode)) { // 老的开始节点与新的结束节点作比较        patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)        canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))        oldStartVnode = oldCh[++oldStartIdx]        newEndVnode = newCh[--newEndIdx]      } else if (sameVnode(oldEndVnode, newStartVnode)) { // 老的结束节点与新的开始节点作比较        patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)        canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)        oldEndVnode = oldCh[--oldEndIdx]        newStartVnode = newCh[++newStartIdx]      } else {        if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)        idxInOld = isDef(newStartVnode.key)          ? oldKeyToIdx[newStartVnode.key]          : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)        if (isUndef(idxInOld)) { // New element          createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)        } else {          vnodeToMove = oldCh[idxInOld]          if (sameVnode(vnodeToMove, newStartVnode)) {            patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)            oldCh[idxInOld] = undefined            canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)          } else {            // same key but different element. treat as new element            createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)          }        }        newStartVnode = newCh[++newStartIdx]      }    }    if (oldStartIdx > oldEndIdx) {      refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm      addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)    } else if (newStartIdx > newEndIdx) {      removeVnodes(oldCh, oldStartIdx, oldEndIdx)    }  }
    
    function sameVnode (a, b) {  return (    a.key === b.key && (      (        a.tag === b.tag &&        a.isComment === b.isComment &&        isDef(a.data) === isDef(b.data) &&        sameInputType(a, b)      ) || (        isTrue(a.isAsyncPlaceholder) &&        a.asyncFactory === b.asyncFactory &&        isUndef(b.asyncFactory.error)      )    )  )}
    

    假如没有使用key情况,首先我们去daptch  A的时候会走进else if (sameVnode(oldStartVnode, newStartVnode)) { // 老的开头节点与新的开头节点比较

    的判断里面,然后执行sameVnode方法,来看下sameVnode方法里面的逻辑;

    首先判断新老数据的key是否相同,因为在没有使用key的情况下a.key与b.key都是undefined所以第一层判断为true;然后再来看下tag标签是否相同,毋庸置疑,标签也是相同的。也都不是注释,data也没有发生变化,并且也不是input标签,那么就会认为这两个是相同的节点,就开始打补丁。一次类推,每一步都会走的这一层,就会被强制更新

    当有key的情况呢,当patch完B的时候,就会走进else if (sameVnode(oldEndVnode, newEndVnode)) { // 老的末尾节点与新的末尾节点作比较  这层判断,然后以此类推。走进最后一个循环结束的 时候,就已经剩下E了,就会走进这个判断里

    if (oldStartIdx > oldEndIdx) {

    // 将新的索引值加1,也就是要找到要追加哪个元素的前面还是后面

    refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm

    addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)

    } else if (newStartIdx > newEndIdx) {

    removeVnodes(oldCh, oldStartIdx, oldEndIdx)

    }

    这个时候oldStartIdx是2,oldEndIdx是1,所以进入判断。执行addVnodes方法,进行节点的追加

    结论:key的作用主要是高效的更新虚拟dom,从源码层面解释,执行的是daptch.js里面的updateChildren方法,进行节点之间的patch操作,根据判断新老节点使用someVnode方法,首先判断key是否相同,再判断标签,如果没有key的情况,就会认为是相同的节点进行强制更新,如果有key的情况,会根据key是否相同进行更新


    起源地下载网 » 浅谈vue中key的作用及工作原理

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元