久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      深入理解vue2中的VNode和diff算法

      深入理解vue2中的VNode和diff算法

      node.js極速入門課程:進(jìn)入學(xué)習(xí)

      虛擬domdiff算法是vue學(xué)習(xí)過程中的一個難點,也是面試中必須掌握的一個知識點。這兩者相輔相成,是vue框架的核心。今天我們再來總結(jié)下vue2中的虛擬domdiff算法。(學(xué)習(xí)視頻分享:vue視頻教程)

      什么是 VNode

      我們知道,render function 會被轉(zhuǎn)化成 VNode。VNode 其實就是一棵以 JavaScript 對象作為基礎(chǔ)的樹,用對象屬性來描述節(jié)點,實際上它只是一層對真實 DOM 的抽象。最終可以通過一系列操作使這棵樹映射到真實環(huán)境上。

      比如有如下template

      <template>   <span class="demo" v-show="isShow"> This is a span. </span>  </template>
      登錄后復(fù)制

      它換成 VNode 以后大概就是下面這個樣子

      {   tag: "span",   data: {     /* 指令集合數(shù)組 */     directives: [       {         /* v-show指令 */         rawName: "v-show",         expression: "isShow",         name: "show",         value: true,       },     ],     /* 靜態(tài)class */     staticClass: "demo",   },   text: undefined,   children: [     /* 子節(jié)點是一個文本VNode節(jié)點 */     {       tag: undefined,       data: undefined,       text: "This is a span.",       children: undefined,     },   ], };
      登錄后復(fù)制

      總的來說,VNode 就是一個 JavaScript 對象。這個JavaScript 對象能完整地表示出真實DOM。

      為什么vue要使用 VNode

      筆者認(rèn)為有兩點原因

      • 由于 Virtual DOM 是以 JavaScript 對象為基礎(chǔ)而不依賴真實平臺環(huán)境,所以使它具有了跨平臺的能力,比如說瀏覽器平臺、Weex、Node 等。

      • 減少操作DOM,任何頁面的變化,都只使用VNode進(jìn)行操作對比,只需要在最后一次進(jìn)行掛載更新DOM,避免了頻繁操作DOM,減少了瀏覽器的回流和重繪從而提高頁面性能。

      diff算法

      下面我們來看看組件更新所涉及到的diff算法

      前面我們講依賴收集的時候有說到,渲染watcher傳遞給Watcherget方法其實是updateComponent方法。

      updateComponent = () => {   vm._update(vm._render(), hydrating) }  new Watcher(vm, updateComponent, noop, {   before () {     if (vm._isMounted) {       callHook(vm, 'beforeUpdate')     }   } }, true /* isRenderWatcher */)
      登錄后復(fù)制

      所以組件在響應(yīng)式數(shù)據(jù)發(fā)生變化的時候會再次觸發(fā)該方法,接下來我們來詳細(xì)分析一下updateComponent里面的_update方法。

      _update

      _update方法中做了初始渲染和更新的區(qū)分,雖然都是調(diào)用__patch__方法,但是傳遞的參數(shù)不一樣。

      // src/core/instance/lifecycle.js  Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {   const vm: Component = this   const prevEl = vm.$el   const prevVnode = vm._vnode   vm._vnode = vnode   // 初次渲染沒有 prevVnode,組件更新才會有   if (!prevVnode) {     // 初次渲染     vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)   } else {     // 更新     vm.$el = vm.__patch__(prevVnode, vnode)   }      // ... }
      登錄后復(fù)制

      下面我們再來看看__patch__方法

      __patch__

      patch方法接收四個參數(shù),由于初始渲染的時候oldVnodevm.$elnull,所以初始渲染是沒有oldVnode

      // src/core/vdom/patch.js  return function patch (oldVnode, vnode, hydrating, removeOnly) {   // 新節(jié)點不存在,只有oldVnode就直接銷毀,然后返回   if (isUndef(vnode)) {     if (isDef(oldVnode)) invokeDestroyHook(oldVnode)     return   }    let isInitialPatch = false   const insertedVnodeQueue = []   // 沒有老節(jié)點,直接創(chuàng)建,也就是初始渲染   if (isUndef(oldVnode)) {     isInitialPatch = true     createElm(vnode, insertedVnodeQueue)   } else {     const isRealElement = isDef(oldVnode.nodeType)     // 不是真實dom,并且是相同節(jié)點走patch     if (!isRealElement && sameVnode(oldVnode, vnode)) {       // 這里才會涉及到diff算法       patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)     } else {       if (isRealElement) {         // ...       }        // replacing existing element       const oldElm = oldVnode.elm       const parentElm = nodeOps.parentNode(oldElm)        // 1.創(chuàng)建一個新節(jié)點       createElm(         vnode,         insertedVnodeQueue,         // extremely rare edge case: do not insert if old element is in a         // leaving transition. Only happens when combining transition +         // keep-alive + HOCs. (#4590)         oldElm._leaveCb ? null : parentElm,         nodeOps.nextSibling(oldElm)       )        // 2.更新父節(jié)點占位符       if (isDef(vnode.parent)) {         let ancestor = vnode.parent         const patchable = isPatchable(vnode)         while (ancestor) {           for (let i = 0; i < cbs.destroy.length; ++i) {             cbs.destroy[i](ancestor)           }           ancestor.elm = vnode.elm           if (patchable) {             for (let i = 0; i < cbs.create.length; ++i) {               cbs.create[i](emptyNode, ancestor)             }                          const insert = ancestor.data.hook.insert             if (insert.merged) {               // start at index 1 to avoid re-invoking component mounted hook               for (let i = 1; i < insert.fns.length; i++) {                 insert.fns[i]()               }             }           } else {             registerRef(ancestor)           }           ancestor = ancestor.parent         }       }        // 3.刪除老節(jié)點       if (isDef(parentElm)) {         removeVnodes([oldVnode], 0, 0)       } else if (isDef(oldVnode.tag)) {         invokeDestroyHook(oldVnode)       }     }   }        //觸發(fā)插入鉤子   invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)   return vnode.elm }
      登錄后復(fù)制

      patch方法大概流程如下:

      • 沒有新節(jié)點只有老節(jié)點直接刪除老節(jié)點。

      • 只有新節(jié)點沒有老節(jié)點直接添加新節(jié)點。

      • 既有新節(jié)點又有老節(jié)點則判斷是不是相同節(jié)點,相同則進(jìn)入pathVnode。patchVnode我們后面會重點分析。

      • 既有新節(jié)點又有老節(jié)點則判斷是不是相同節(jié)點,不相同則直接刪除老節(jié)點添加新節(jié)點。

      我們再來看看它是怎么判斷是同一個節(jié)點的。

      // src/core/vdom/patch.js  function sameVnode (a, b) {   return (     a.key === b.key &&     a.asyncFactory === b.asyncFactory && (       (         a.tag === b.tag &&         a.isComment === b.isComment &&         isDef(a.data) === isDef(b.data) &&         sameInputType(a, b)       ) || (         isTrue(a.isAsyncPlaceholder) &&         isUndef(b.asyncFactory.error)       )     )   ) }  function sameInputType (a, b) {   if (a.tag !== 'input') return true   let i   const typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type   const typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type   return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB) }
      登錄后復(fù)制

      判斷兩個VNode節(jié)點是否是同一個節(jié)點,需要同時滿足以下條件

      • key相同

      • 都有異步組件工廠函數(shù)

      • tag(當(dāng)前節(jié)點的標(biāo)簽名)相同

      • isComment是否同為注釋節(jié)點

      • 是否data(當(dāng)前節(jié)點對應(yīng)的對象,包含了具體的一些數(shù)據(jù)信息,是一個VNodeData類型)

      • 當(dāng)標(biāo)簽是<input>的時候,type必須相同

      當(dāng)兩個VNodetag、key、isComment都相同,并且同時定義或未定義data的時候,且如果標(biāo)簽為input則type必須相同。這時候這兩個VNode則算sameVnode,可以直接進(jìn)行patchVnode操作。

      patchVnode

      下面我們再來詳細(xì)分析下patchVnode方法。

      // src/core/vdom/patch.js  function patchVnode (   oldVnode,   vnode,   insertedVnodeQueue,   ownerArray,   index,   removeOnly ) {   // 兩個vnode相同則直接返回   if (oldVnode === vnode) {     return   }    if (isDef(vnode.elm) && isDef(ownerArray)) {     // clone reused vnode     vnode = ownerArray[index] = cloneVNode(vnode)   }    const elm = vnode.elm = oldVnode.elm    if (isTrue(oldVnode.isAsyncPlaceholder)) {     if (isDef(vnode.asyncFactory.resolved)) {       hydrate(oldVnode.elm, vnode, insertedVnodeQueue)     } else {       vnode.isAsyncPlaceholder = true     }     return   }    /*     如果新舊VNode都是靜態(tài)的,同時它們的key相同(代表同一節(jié)點),     并且新的VNode是clone或者是標(biāo)記了once(標(biāo)記v-once屬性,只渲染一次),     那么只需要替換componentInstance即可。   */   if (isTrue(vnode.isStatic) &&     isTrue(oldVnode.isStatic) &&     vnode.key === oldVnode.key &&     (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))   ) {     vnode.componentInstance = oldVnode.componentInstance     return   }    let i   const data = vnode.data   /*調(diào)用prepatch鉤子*/   if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {     i(oldVnode, vnode)   }    // 獲取新老虛擬節(jié)點的子節(jié)點   const oldCh = oldVnode.children   const ch = vnode.children   if (isDef(data) && isPatchable(vnode)) {     for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)     if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)   }      // 新節(jié)點不是文本節(jié)點   if (isUndef(vnode.text)) {     /*新老節(jié)點均有children子節(jié)點,則對子節(jié)點進(jìn)行diff操作,調(diào)用updateChildren*/     if (isDef(oldCh) && isDef(ch)) {       if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)     /*如果只有新節(jié)點有子節(jié)點,先清空elm文本內(nèi)容,然后為當(dāng)前DOM節(jié)點加入子節(jié)點。*/     } else if (isDef(ch)) {       if (process.env.NODE_ENV !== 'production') {         checkDuplicateKeys(ch)       }       if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')       addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)     /*如果只有老節(jié)點有子節(jié)點,則移除elm所有子節(jié)點*/     } else if (isDef(oldCh)) {       removeVnodes(oldCh, 0, oldCh.length - 1)     /*當(dāng)新老節(jié)點都無子節(jié)點的時候,因為這個邏輯中新節(jié)點text不存在,所以直接去除ele的文本*/     } else if (isDef(oldVnode.text)) {       nodeOps.setTextContent(elm, '')     }   // 新節(jié)點是文本節(jié)點,如果文本不一樣就設(shè)置新的文本     } else if (oldVnode.text !== vnode.text) {     nodeOps.setTextContent(elm, vnode.text)   }   /*調(diào)用postpatch鉤子*/   if (isDef(data)) {     if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)   } }
      登錄后復(fù)制

      patchVnode方法大概流程如下:

      1.新老節(jié)點相同就直接返回。

      2.如果新舊VNode都是靜態(tài)的,同時它們的key相同(代表同一節(jié)點),并且新的VNode是clone或者是標(biāo)記了once(標(biāo)記v-once屬性,只渲染一次),那么只需要替換componentInstance即可。

      3.新節(jié)點不是文本節(jié)點,新老節(jié)點均有children子節(jié)點,則對子節(jié)點進(jìn)行diff操作,調(diào)用updateChildren,這個updateChildrendiff算法的核心,后面我們會重點說。

      4.新節(jié)點不是文本節(jié)點,如果老節(jié)點沒有子節(jié)點而新節(jié)點存在子節(jié)點,先清空老節(jié)點DOM的文本內(nèi)容,然后為當(dāng)前DOM節(jié)點加入子節(jié)點。

      5.新節(jié)點不是文本節(jié)點,當(dāng)新節(jié)點沒有子節(jié)點而老節(jié)點有子節(jié)點的時候,則移除該DOM節(jié)點的所有子節(jié)點。

      6.新節(jié)點不是文本節(jié)點,并且新老節(jié)點都無子節(jié)點的時候,只需要將老節(jié)點文本清空。

      7.新節(jié)點是文本節(jié)點,并且新老節(jié)點文本不一樣,則進(jìn)行文本的替換。

      updateChildren(diff算法核心)

      updateChildrendiff算法的核心,下面我們來重點分析。

      深入理解vue2中的VNode和diff算法

      這兩張圖代表舊的VNode與新VNode進(jìn)行patch的過程,他們只是在同層級的VNode之間進(jìn)行比較得到變化(相同顏色的方塊代表互相進(jìn)行比較的VNode節(jié)點),然后修改變化的視圖,所以十分高效。所以Diff算法是:深度優(yōu)先算法。 時間復(fù)雜度:O(n)

      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    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]     // 老 VNode 節(jié)點的頭部與新 VNode 節(jié)點的頭部是相同的 VNode 節(jié)點,直接進(jìn)行 patchVnode,同時 oldStartIdx 與 newStartIdx 向后移動一位。     } else if (sameVnode(oldStartVnode, newStartVnode)) {       patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)       oldStartVnode = oldCh[++oldStartIdx]       newStartVnode = newCh[++newStartIdx]     // 兩個 VNode 的結(jié)尾是相同的 VNode,同樣進(jìn)行 patchVnode 操作。并將 oldEndVnode 與 newEndVnode 向前移動一位。     } else if (sameVnode(oldEndVnode, newEndVnode)) {       patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)       oldEndVnode = oldCh[--oldEndIdx]       newEndVnode = newCh[--newEndIdx]     // oldStartVnode 與 newEndVnode 符合 sameVnode 的時候,     // 將 oldStartVnode.elm 這個節(jié)點直接移動到 oldEndVnode.elm 這個節(jié)點的后面即可。     // 然后 oldStartIdx 向后移動一位,newEndIdx 向前移動一位。     } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right       patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)       canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))       oldStartVnode = oldCh[++oldStartIdx]       newEndVnode = newCh[--newEndIdx]     // oldEndVnode 與 newStartVnode 符合 sameVnode 時,     // 將 oldEndVnode.elm 插入到 oldStartVnode.elm 前面。     // oldEndIdx 向前移動一位,newStartIdx 向后移動一位。     } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left       patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)       canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)       oldEndVnode = oldCh[--oldEndIdx]       newStartVnode = newCh[++newStartIdx]     } else {       // createKeyToOldIdx 的作用是產(chǎn)生 key 與 index 索引對應(yīng)的一個 map 表       if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)       idxInOld = isDef(newStartVnode.key)         ? oldKeyToIdx[newStartVnode.key]         : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)       // 如果沒有找到相同的節(jié)點,則通過 createElm 創(chuàng)建一個新節(jié)點,并將 newStartIdx 向后移動一位。       if (isUndef(idxInOld)) { // New element         createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)       } else {         vnodeToMove = oldCh[idxInOld]         // 如果找到了節(jié)點,同時它符合 sameVnode,則將這兩個節(jié)點進(jìn)行 patchVnode,將該位置的老節(jié)點賦值 undefined         // 同時將 newStartVnode.elm 插入到 oldStartVnode.elm 的前面         if (sameVnode(vnodeToMove, newStartVnode)) {           patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)           oldCh[idxInOld] = undefined           canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)         } else {           // 如果不符合 sameVnode,只能創(chuàng)建一個新節(jié)點插入到 parentElm 的子節(jié)點中,newStartIdx 往后移動一位。           createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)         }       }       newStartVnode = newCh[++newStartIdx]     }   }   // 當(dāng) while 循環(huán)結(jié)束以后,如果 oldStartIdx > oldEndIdx,說明老節(jié)點比對完了,但是新節(jié)點還有多的,   // 需要將新節(jié)點插入到真實 DOM 中去,調(diào)用 addVnodes 將這些節(jié)點插入即可。   if (oldStartIdx > oldEndIdx) {     refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm     addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)   // 如果滿足 newStartIdx > newEndIdx 條件,說明新節(jié)點比對完了,老節(jié)點還有多,   // 將這些無用的老節(jié)點通過 removeVnodes 批量刪除即可。   } else if (newStartIdx > newEndIdx) {     removeVnodes(oldCh, oldStartIdx, oldEndIdx)   } }
      登錄后復(fù)制

      vue2diff算法采用的是雙端比較,所謂雙端比較就是新列表舊列表兩個列表的頭與尾互相對比,在對比的過程中指針會逐漸向內(nèi)靠攏,直到某一個列表的節(jié)點全部遍歷過,對比停止。

      首尾對比的四種情況

      我們首先來看看首尾對比的四種情況。

      • 使用舊列表的頭一個節(jié)點oldStartNode新列表的頭一個節(jié)點newStartNode對比

      • 使用舊列表的最后一個節(jié)點oldEndNode新列表的最后一個節(jié)點newEndNode對比

      • 使用舊列表的頭一個節(jié)點oldStartNode新列表的最后一個節(jié)點newEndNode對比

      • 使用舊列表的最后一個節(jié)點oldEndNode新列表的頭一個節(jié)點newStartNode對比

      首先是 oldStartVnodenewStartVnode 符合 sameVnode 時,說明老 VNode 節(jié)點的頭部與新 VNode 節(jié)點的頭部是相同的 VNode 節(jié)點,直接進(jìn)行 patchVnode,同時 oldStartIdxnewStartIdx 向后移動一位。

      深入理解vue2中的VNode和diff算法

      其次是 oldEndVnodenewEndVnode 符合 sameVnode,也就是兩個 VNode 的結(jié)尾是相同的 VNode,同樣進(jìn)行 patchVnode 操作并將 oldEndVnodenewEndVnode 向前移動一位。

      深入理解vue2中的VNode和diff算法

      接下來是兩種交叉的情況。

      先是 oldStartVnodenewEndVnode 符合 sameVnode 的時候,也就是老 VNode 節(jié)點的頭部與新 VNode 節(jié)點的尾部是同一節(jié)點的時候,將 oldStartVnode.elm 這個節(jié)點直接移動到 oldEndVnode.elm 這個節(jié)點的后面即可。然后 oldStartIdx 向后移動一位,newEndIdx 向前移動一位。

      深入理解vue2中的VNode和diff算法

      同理,oldEndVnodenewStartVnode 符合 sameVnode 時,也就是老 VNode 節(jié)點的尾部與新 VNode 節(jié)點的頭部是同一節(jié)點的時候,將 oldEndVnode.elm 插入到 oldStartVnode.elm 前面。同樣的,oldEndIdx 向前移動一位,newStartIdx 向后移動一位。

      深入理解vue2中的VNode和diff算法

      最后是當(dāng)以上情況都不符合的時候,這種情況怎么處理呢?

      查找對比

      那就是查找對比。

      首先通過createKeyToOldIdx方法生成oldVnodekeyindex 索引對應(yīng)的一個 map 表。

      然后我們根據(jù)newStartVnode.key,可以快速地從 oldKeyToIdxcreateKeyToOldIdx 的返回值)中獲取相同 key 的節(jié)點的索引 idxInOld,然后找到相同的節(jié)點。

      這里又分三種情況

      • 如果沒有找到相同的節(jié)點,則通過 createElm 創(chuàng)建一個新節(jié)點,并將 newStartIdx 向后移動一位。

      • 如果找到了節(jié)點,同時它符合 sameVnode,則將這兩個節(jié)點進(jìn)行 patchVnode,將該位置的老節(jié)點賦值 undefined(之后如果還有新節(jié)點與該節(jié)點key相同可以檢測出來提示已有重復(fù)的 key ),同時將 newStartVnode.elm 插入到 oldStartVnode.elm 的前面。同理,newStartIdx 往后移動一位。

      深入理解vue2中的VNode和diff算法

      • 如果不符合 sameVnode,只能創(chuàng)建一個新節(jié)點插入到 parentElm 的子節(jié)點中,newStartIdx 往后移動一位。

      深入理解vue2中的VNode和diff算法

      添加、刪除節(jié)點

      最后一步就很容易啦,當(dāng) while 循環(huán)結(jié)束以后,如果 oldStartIdx > oldEndIdx,說明老節(jié)點比對完了,但是新節(jié)點還有多的,需要將新節(jié)點插入到真實 DOM 中去,調(diào)用 addVnodes 將這些節(jié)點插入即可。

      深入理解vue2中的VNode和diff算法

      同理,如果滿足 newStartIdx > newEndIdx 條件,說明新節(jié)點比對完了,老節(jié)點還有多,將這些無用的老節(jié)點通過 removeVnodes 批量刪除即可。

      深入理解vue2中的VNode和diff算法

      總結(jié)

      Diff算法是一種對比算法。對比兩者是舊虛擬DOM和新虛擬DOM,對比出是哪個虛擬節(jié)點更改了,找出這個虛擬節(jié)點,并只更新這個虛擬節(jié)點所對應(yīng)的真實節(jié)點,而不用更新其他數(shù)據(jù)沒發(fā)生改變的節(jié)點,實現(xiàn)精準(zhǔn)地更新真實DOM,進(jìn)而提高效率和性能。

      精準(zhǔn)主要體現(xiàn)在,diff 算法首先就是找到可復(fù)用的節(jié)點,然后移動到正確的位置。當(dāng)元素沒有找到的話再來創(chuàng)建新節(jié)點。

      擴展

      vue中為什么需要使用key,它的作用是什么?

      keyVuevnode 的唯一標(biāo)記,通過這個 key,diff 操作可以更準(zhǔn)確、更快速。

      1. 更準(zhǔn)確:因為帶 key 就不是就地復(fù)用了,在 sameNode 函數(shù) a.key === b.key 對比中可以避免就地復(fù)用的情況。所以會更加準(zhǔn)確。
      2. 更快速:利用 key 的唯一性生成 map 對象來獲取對應(yīng)節(jié)點,比遍歷方式更快。

      為什么不推薦使用index作為key

      當(dāng)我們的列表只涉及到 展示,不涉及到排序、刪除、添加的時候使用index作為key是沒什么問題的。因為此時的index在每個元素上是唯一的。

      但是如果涉及到排序、刪除、添加的時候就不能再使用index作為key了,因為每個元素key不再唯一了。不唯一的key,對diff算法沒有任何幫助,寫和沒寫是一樣的。

      (學(xué)習(xí)視頻分享:web前端開發(fā)、編程基礎(chǔ)視頻)

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號