久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放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. 站長(zhǎng)資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      你了解vue diff算法嗎?原理解析

      diff算法是一種通過(guò)同層的樹(shù)節(jié)點(diǎn)進(jìn)行比較的高效算法,避免了對(duì)樹(shù)進(jìn)行逐層搜索遍歷。那么大家對(duì)diff算法嗎有多少了解?下面本篇文章就來(lái)帶大家深入解析下vue的diff算法,希望對(duì)大家有所幫助!

      你了解vue diff算法嗎?原理解析

      一、是什么

      diff 算法是一種通過(guò)同層的樹(shù)節(jié)點(diǎn)進(jìn)行比較的高效算法。(學(xué)習(xí)視頻分享:vue視頻教程)

      其有兩個(gè)特點(diǎn):

      • 比較只會(huì)在同層級(jí)進(jìn)行, 不會(huì)跨層級(jí)比較
      • 在diff比較的過(guò)程中,循環(huán)從兩邊向中間比較

      diff 算法在很多場(chǎng)景下都有應(yīng)用,在 vue 中,作用于虛擬 dom 渲染成真實(shí) dom 的新舊 VNode 節(jié)點(diǎn)比較

      二、比較方式

      diff整體策略為:深度優(yōu)先,同層比較

      • 比較只會(huì)在同層級(jí)進(jìn)行, 不會(huì)跨層級(jí)比較

      你了解vue diff算法嗎?原理解析

      • 比較的過(guò)程中,循環(huán)從兩邊向中間收攏

      你了解vue diff算法嗎?原理解析

      下面舉個(gè)vue通過(guò)diff算法更新的例子:

      新舊VNode節(jié)點(diǎn)如下圖所示:

      你了解vue diff算法嗎?原理解析

      第一次循環(huán)后,發(fā)現(xiàn)舊節(jié)點(diǎn)D與新節(jié)點(diǎn)D相同,直接復(fù)用舊節(jié)點(diǎn)D作為diff后的第一個(gè)真實(shí)節(jié)點(diǎn),同時(shí)舊節(jié)點(diǎn)endIndex移動(dòng)到C,新節(jié)點(diǎn)的 startIndex 移動(dòng)到了 C

      你了解vue diff算法嗎?原理解析

      第二次循環(huán)后,同樣是舊節(jié)點(diǎn)的末尾和新節(jié)點(diǎn)的開(kāi)頭(都是 C)相同,同理,diff 后創(chuàng)建了 C 的真實(shí)節(jié)點(diǎn)插入到第一次創(chuàng)建的 D 節(jié)點(diǎn)后面。同時(shí)舊節(jié)點(diǎn)的 endIndex 移動(dòng)到了 B,新節(jié)點(diǎn)的 startIndex 移動(dòng)到了 E

      你了解vue diff算法嗎?原理解析

      第三次循環(huán)中,發(fā)現(xiàn)E沒(méi)有找到,這時(shí)候只能直接創(chuàng)建新的真實(shí)節(jié)點(diǎn) E,插入到第二次創(chuàng)建的 C 節(jié)點(diǎn)之后。同時(shí)新節(jié)點(diǎn)的 startIndex 移動(dòng)到了 A。舊節(jié)點(diǎn)的 startIndexendIndex 都保持不動(dòng)

      你了解vue diff算法嗎?原理解析

      第四次循環(huán)中,發(fā)現(xiàn)了新舊節(jié)點(diǎn)的開(kāi)頭(都是 A)相同,于是 diff 后創(chuàng)建了 A 的真實(shí)節(jié)點(diǎn),插入到前一次創(chuàng)建的 E 節(jié)點(diǎn)后面。同時(shí)舊節(jié)點(diǎn)的 startIndex 移動(dòng)到了 B,新節(jié)點(diǎn)的startIndex 移動(dòng)到了 B

      你了解vue diff算法嗎?原理解析

      第五次循環(huán)中,情形同第四次循環(huán)一樣,因此 diff 后創(chuàng)建了 B 真實(shí)節(jié)點(diǎn) 插入到前一次創(chuàng)建的 A 節(jié)點(diǎn)后面。同時(shí)舊節(jié)點(diǎn)的 startIndex移動(dòng)到了 C,新節(jié)點(diǎn)的 startIndex 移動(dòng)到了 F

      你了解vue diff算法嗎?原理解析

      新節(jié)點(diǎn)的 startIndex 已經(jīng)大于 endIndex 了,需要?jiǎng)?chuàng)建 newStartIdxnewEndIdx 之間的所有節(jié)點(diǎn),也就是節(jié)點(diǎn)F,直接創(chuàng)建 F 節(jié)點(diǎn)對(duì)應(yīng)的真實(shí)節(jié)點(diǎn)放到 B 節(jié)點(diǎn)后面

      你了解vue diff算法嗎?原理解析

      三、原理分析

      當(dāng)數(shù)據(jù)發(fā)生改變時(shí),set方法會(huì)調(diào)用Dep.notify通知所有訂閱者Watcher,訂閱者就會(huì)調(diào)用patch給真實(shí)的DOM打補(bǔ)丁,更新相應(yīng)的視圖

      源碼位置:src/core/vdom/patch.js

      function patch(oldVnode, vnode, hydrating, removeOnly) {     if (isUndef(vnode)) { // 沒(méi)有新節(jié)點(diǎn),直接執(zhí)行destory鉤子函數(shù)         if (isDef(oldVnode)) invokeDestroyHook(oldVnode)         return     }      let isInitialPatch = false     const insertedVnodeQueue = []      if (isUndef(oldVnode)) {         isInitialPatch = true         createElm(vnode, insertedVnodeQueue) // 沒(méi)有舊節(jié)點(diǎn),直接用新節(jié)點(diǎn)生成dom元素     } else {         const isRealElement = isDef(oldVnode.nodeType)         if (!isRealElement && sameVnode(oldVnode, vnode)) {             // 判斷舊節(jié)點(diǎn)和新節(jié)點(diǎn)自身一樣,一致執(zhí)行patchVnode             patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)         } else {             // 否則直接銷(xiāo)毀及舊節(jié)點(diǎn),根據(jù)新節(jié)點(diǎn)生成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函數(shù)前兩個(gè)參數(shù)位為oldVnodeVnode ,分別代表新的節(jié)點(diǎn)和之前的舊節(jié)點(diǎn),主要做了四個(gè)判斷:

      • 沒(méi)有新節(jié)點(diǎn),直接觸發(fā)舊節(jié)點(diǎn)的destory鉤子
      • 沒(méi)有舊節(jié)點(diǎn),說(shuō)明是頁(yè)面剛開(kāi)始初始化的時(shí)候,此時(shí),根本不需要比較了,直接全是新建,所以只調(diào)用 createElm
      • 舊節(jié)點(diǎn)和新節(jié)點(diǎn)自身一樣,通過(guò) sameVnode 判斷節(jié)點(diǎn)是否一樣,一樣時(shí),直接調(diào)用 patchVnode去處理這兩個(gè)節(jié)點(diǎn)
      • 舊節(jié)點(diǎn)和新節(jié)點(diǎn)自身不一樣,當(dāng)兩個(gè)節(jié)點(diǎn)不一樣的時(shí)候,直接創(chuàng)建新節(jié)點(diǎn),刪除舊節(jié)點(diǎn)

      下面主要講的是patchVnode部分

      function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {     // 如果新舊節(jié)點(diǎn)一致,什么都不做     if (oldVnode === vnode) {       return     }     // 讓vnode.el引用到現(xiàn)在的真實(shí)dom,當(dāng)el修改時(shí),vnode.el會(huì)同步變化     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     }     // 如果新舊都是靜態(tài)節(jié)點(diǎn),并且具有相同的key     // 當(dāng)vnode是克隆節(jié)點(diǎn)或是v-once指令控制的節(jié)點(diǎn)時(shí),只需要把oldVnode.elm和oldVnode.child都復(fù)制到vnode上     // 也不用再有其他操作     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     if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {       i(oldVnode, vnode)     }     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)     }     // 如果vnode不是文本節(jié)點(diǎn)或者注釋節(jié)點(diǎn)     if (isUndef(vnode.text)) {       // 并且都有子節(jié)點(diǎn)       if (isDef(oldCh) && isDef(ch)) {         // 并且子節(jié)點(diǎn)不完全一致,則調(diào)用updateChildren         if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)         // 如果只有新的vnode有子節(jié)點(diǎn)       } else if (isDef(ch)) {         if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')         // elm已經(jīng)引用了老的dom節(jié)點(diǎn),在老的dom節(jié)點(diǎn)上添加子節(jié)點(diǎn)         addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)         // 如果新vnode沒(méi)有子節(jié)點(diǎn),而vnode有子節(jié)點(diǎn),直接刪除老的oldCh       } else if (isDef(oldCh)) {         removeVnodes(elm, oldCh, 0, oldCh.length - 1)         // 如果老節(jié)點(diǎn)是文本節(jié)點(diǎn)       } else if (isDef(oldVnode.text)) {         nodeOps.setTextContent(elm, '')       }       // 如果新vnode和老vnode是文本節(jié)點(diǎn)或注釋節(jié)點(diǎn)       // 但是vnode.text != oldVnode.text時(shí),只需要更新vnode.elm的文本內(nèi)容就可以     } else if (oldVnode.text !== vnode.text) {       nodeOps.setTextContent(elm, vnode.text)     }     if (isDef(data)) {       if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)     }   }

      patchVnode主要做了幾個(gè)判斷:

      • 新節(jié)點(diǎn)是否是文本節(jié)點(diǎn),如果是,則直接更新dom的文本內(nèi)容為新節(jié)點(diǎn)的文本內(nèi)容
      • 新節(jié)點(diǎn)和舊節(jié)點(diǎn)如果都有子節(jié)點(diǎn),則處理比較更新子節(jié)點(diǎn)
      • 只有新節(jié)點(diǎn)有子節(jié)點(diǎn),舊節(jié)點(diǎn)沒(méi)有,那么不用比較了,所有節(jié)點(diǎn)都是全新的,所以直接全部新建就好了,新建是指創(chuàng)建出所有新DOM,并且添加進(jìn)父節(jié)點(diǎn)
      • 只有舊節(jié)點(diǎn)有子節(jié)點(diǎn)而新節(jié)點(diǎn)沒(méi)有,說(shuō)明更新后的頁(yè)面,舊節(jié)點(diǎn)全部都不見(jiàn)了,那么要做的,就是把所有的舊節(jié)點(diǎn)刪除,也就是直接把DOM 刪除

      子節(jié)點(diǎn)不完全一致,則調(diào)用updateChildren

      function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {     let oldStartIdx = 0 // 舊頭索引     let newStartIdx = 0 // 新頭索引     let oldEndIdx = oldCh.length - 1 // 舊尾索引     let newEndIdx = newCh.length - 1 // 新尾索引     let oldStartVnode = oldCh[0] // oldVnode的第一個(gè)child     let oldEndVnode = oldCh[oldEndIdx] // oldVnode的最后一個(gè)child     let newStartVnode = newCh[0] // newVnode的第一個(gè)child     let newEndVnode = newCh[newEndIdx] // newVnode的最后一個(gè)child     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     // 如果oldStartVnode和oldEndVnode重合,并且新的也都重合了,證明diff完了,循環(huán)結(jié)束     while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {       // 如果oldVnode的第一個(gè)child不存在       if (isUndef(oldStartVnode)) {         // oldStart索引右移         oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left       // 如果oldVnode的最后一個(gè)child不存在       } else if (isUndef(oldEndVnode)) {         // oldEnd索引左移         oldEndVnode = oldCh[--oldEndIdx]       // oldStartVnode和newStartVnode是同一個(gè)節(jié)點(diǎn)       } else if (sameVnode(oldStartVnode, newStartVnode)) {         // patch oldStartVnode和newStartVnode, 索引左移,繼續(xù)循環(huán)         patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue)         oldStartVnode = oldCh[++oldStartIdx]         newStartVnode = newCh[++newStartIdx]       // oldEndVnode和newEndVnode是同一個(gè)節(jié)點(diǎn)       } else if (sameVnode(oldEndVnode, newEndVnode)) {         // patch oldEndVnode和newEndVnode,索引右移,繼續(xù)循環(huán)         patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue)         oldEndVnode = oldCh[--oldEndIdx]         newEndVnode = newCh[--newEndIdx]       // oldStartVnode和newEndVnode是同一個(gè)節(jié)點(diǎn)       } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right         // patch oldStartVnode和newEndVnode         patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)         // 如果removeOnly是false,則將oldStartVnode.eml移動(dòng)到oldEndVnode.elm之后         canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))         // oldStart索引右移,newEnd索引左移         oldStartVnode = oldCh[++oldStartIdx]         newEndVnode = newCh[--newEndIdx]       // 如果oldEndVnode和newStartVnode是同一個(gè)節(jié)點(diǎn)       } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left         // patch oldEndVnode和newStartVnode         patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)         // 如果removeOnly是false,則將oldEndVnode.elm移動(dòng)到oldStartVnode.elm之前         canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)         // oldEnd索引左移,newStart索引右移         oldEndVnode = oldCh[--oldEndIdx]         newStartVnode = newCh[++newStartIdx]       // 如果都不匹配       } else {         if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)         // 嘗試在oldChildren中尋找和newStartVnode的具有相同的key的Vnode         idxInOld = isDef(newStartVnode.key)           ? oldKeyToIdx[newStartVnode.key]           : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)         // 如果未找到,說(shuō)明newStartVnode是一個(gè)新的節(jié)點(diǎn)         if (isUndef(idxInOld)) { // New element           // 創(chuàng)建一個(gè)新Vnode           createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)         // 如果找到了和newStartVnodej具有相同的key的Vnode,叫vnodeToMove         } else {           vnodeToMove = oldCh[idxInOld]           /* istanbul ignore if */           if (process.env.NODE_ENV !== 'production' && !vnodeToMove) {             warn(               'It seems there are duplicate keys that is causing an update error. ' +               'Make sure each v-for item has a unique key.'             )           }           // 比較兩個(gè)具有相同的key的新節(jié)點(diǎn)是否是同一個(gè)節(jié)點(diǎn)           //不設(shè)key,newCh和oldCh只會(huì)進(jìn)行頭尾兩端的相互比較,設(shè)key后,除了頭尾兩端的比較外,還會(huì)從用key生成的對(duì)象oldKeyToIdx中查找匹配的節(jié)點(diǎn),所以為節(jié)點(diǎn)設(shè)置key可以更高效的利用dom。           if (sameVnode(vnodeToMove, newStartVnode)) {             // patch vnodeToMove和newStartVnode             patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue)             // 清除             oldCh[idxInOld] = undefined             // 如果removeOnly是false,則將找到的和newStartVnodej具有相同的key的Vnode,叫vnodeToMove.elm             // 移動(dòng)到oldStartVnode.elm之前             canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)           // 如果key相同,但是節(jié)點(diǎn)不相同,則創(chuàng)建一個(gè)新的節(jié)點(diǎn)           } else {             // same key but different element. treat as new element             createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)           }         }         // 右移         newStartVnode = newCh[++newStartIdx]       }     }

      while循環(huán)主要處理了以下五種情景:

      • 當(dāng)新老 VNode 節(jié)點(diǎn)的 start 相同時(shí),直接 patchVnode ,同時(shí)新老 VNode 節(jié)點(diǎn)的開(kāi)始索引都加 1
      • 當(dāng)新老 VNode 節(jié)點(diǎn)的 end相同時(shí),同樣直接 patchVnode ,同時(shí)新老 VNode 節(jié)點(diǎn)的結(jié)束索引都減 1
      • 當(dāng)老 VNode 節(jié)點(diǎn)的 start 和新 VNode 節(jié)點(diǎn)的 end 相同時(shí),這時(shí)候在 patchVnode 后,還需要將當(dāng)前真實(shí) dom 節(jié)點(diǎn)移動(dòng)到 oldEndVnode 的后面,同時(shí)老 VNode 節(jié)點(diǎn)開(kāi)始索引加 1,新 VNode 節(jié)點(diǎn)的結(jié)束索引減 1
      • 當(dāng)老 VNode 節(jié)點(diǎn)的 end 和新 VNode 節(jié)點(diǎn)的 start 相同時(shí),這時(shí)候在 patchVnode 后,還需要將當(dāng)前真實(shí) dom 節(jié)點(diǎn)移動(dòng)到 oldStartVnode 的前面,同時(shí)老 VNode 節(jié)點(diǎn)結(jié)束索引減 1,新 VNode 節(jié)點(diǎn)的開(kāi)始索引加 1
      • 如果都不滿足以上四種情形,那說(shuō)明沒(méi)有相同的節(jié)點(diǎn)可以復(fù)用,則會(huì)分為以下兩種情況:
        • 從舊的 VNodekey 值,對(duì)應(yīng) index 序列為 value 值的哈希表中找到與 newStartVnode 一致 key 的舊的 VNode 節(jié)點(diǎn),再進(jìn)行patchVnode,同時(shí)將這個(gè)真實(shí) dom移動(dòng)到 oldStartVnode 對(duì)應(yīng)的真實(shí) dom 的前面
        • 調(diào)用 createElm 創(chuàng)建一個(gè)新的 dom 節(jié)點(diǎn)放到當(dāng)前 newStartIdx 的位置

      小結(jié)

      • 當(dāng)數(shù)據(jù)發(fā)生改變時(shí),訂閱者watcher就會(huì)調(diào)用patch給真實(shí)的DOM打補(bǔ)丁
      • 通過(guò)isSameVnode進(jìn)行判斷,相同則調(diào)用patchVnode方法
      • patchVnode做了以下操作:
        • 找到對(duì)應(yīng)的真實(shí)dom,稱(chēng)為el
        • 如果都有都有文本節(jié)點(diǎn)且不相等,將el文本節(jié)點(diǎn)設(shè)置為Vnode的文本節(jié)點(diǎn)
        • 如果oldVnode有子節(jié)點(diǎn)而VNode沒(méi)有,則刪除el子節(jié)點(diǎn)
        • 如果oldVnode沒(méi)有子節(jié)點(diǎn)而VNode有,則將VNode的子節(jié)點(diǎn)真實(shí)化后添加到el
        • 如果兩者都有子節(jié)點(diǎn),則執(zhí)行updateChildren函數(shù)比較子節(jié)點(diǎn)
      • updateChildren主要做了以下操作:
        • 設(shè)置新舊VNode的頭尾指針
        • 新舊頭尾指針進(jìn)行比較,循環(huán)向中間靠攏,根據(jù)情況調(diào)用patchVnode進(jìn)行patch重復(fù)流程、調(diào)用createElem創(chuàng)建一個(gè)新節(jié)點(diǎn),從哈希表尋找 key一致的VNode 節(jié)點(diǎn)再分情況操作

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

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