<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                >[success] # patch 方法分析(三) ~~~ 1.上一個章節分析后,patch更新主要分為兩塊,一塊是新老虛擬dom相同,一塊是不同,這里是對 相同位置的分析 ~~~ >[info] ## 分析patch 里面patchVnode ~~~ 1.在patch 里如果 oldVnode 和 vnode 相同(key 和 sel 相同),調用 patchVnode(),找節點的差異并更新 DOM 2.在這里主要對比 oldVnode 和 vnode 的差異,把差異渲染到 DOM 3.分析執行過程(分析過程直接看虛擬dom比較的代碼,不分析鉤子部分): 3.1.如果 vnode.text 未定義 3.1.1.如果 oldVnode.children 和 vnode.children 都有值 3.1.1.1.調用 updateChildren() 3.1.1.2.使用 diff 算法對比子節點,更新子節點 3.1.2.如果 vnode.children 有值, oldVnode.children 無值 3.1.2.1.清空 DOM 元素 3.1.2.2.調用 addVnodes() ,批量添加子節點 3.1.3.如果 oldVnode.children 有值, vnode.children 無值 3.1.3.1.調用 removeVnodes() ,批量移除子節點 3.1.4.如果 oldVnode.text 有值 3.1.4.1.清空 DOM 元素的內容 3.2.如果設置了 vnode.text 并且和和 oldVnode.text 不等 3.2.1.如果老節點有子節點,全部移除 3.2.2.設置 DOM 元素的 textContent 為 vnode.text ~~~ ~~~ // 如果 vnode.text 未定義,有text 就沒有children if (isUndef(vnode.text)) { // 如果新老節點都有 children if (isDef(oldCh) && isDef(ch)) { // 使用 diff 算法對比子節點,更新子節點 if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue); } else if (isDef(ch)) { // 如果新節點有 children,老節點沒有 children // 如果老節點有text,清空dom 元素的內容 if (isDef(oldVnode.text)) api.setTextContent(elm, ''); // 批量添加子節點 addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue); } else if (isDef(oldCh)) { // 如果老節點有children,新節點沒有children // 批量移除子節點 removeVnodes(elm, oldCh, 0, oldCh.length - 1); } else if (isDef(oldVnode.text)) { // 如果老節點有 text,清空 DOM 元素 api.setTextContent(elm, ''); } } else if (oldVnode.text !== vnode.text) { // 如果沒有設置 vnode.text if (isDef(oldCh)) { // 如果老節點有 children,移除 removeVnodes(elm, oldCh, 0, oldCh.length - 1); } // 設置 DOM 元素的 textContent 為 vnode.text api.setTextContent(elm, vnode.text!); ~~~ >[info] ## updateChildren -- diff ~~~ 1.'patchVnode'是對具有相同父節點(這里判斷依據是key和sel選擇器相同則認為是相同父節點), 新老虛擬dom此時要比也就是自己的子節點是否相同,其他情況上面分析過,其中最復雜的情況就是 新老子節點都是有children 情況,最笨的方法拿每一個老節點虛擬dom和新節點的去比較,如下圖嵌套三層 的情況下時間復雜度為 O(n^3) 2.如何優化? 2.1.需要先假設一個前提,'在DOM 操作的時候我們很少很少會把一個父節點移動/更新到某一個子節點' 2.2.此時我們要做的就是同級比較即可,每一層和每一層去比較,如果依舊采用的思路是拿每一個老節點 虛擬dom和新節點的去比較,雖然變成了層和層的去比較此時下圖的時間復雜度為 每段O(n^2)比較和,時間上 感覺比之前快了 2.3.現在再換一個思路利用指針進行找同級別的子節點依次比較,然后再找下一級別的節點比較,這樣算法的 時間復 雜度為 O(n) 2.4.計算上的問題解決了,考慮的就是dom復用,有時候重新排序只是新老dom節點位置發生變化,如果能復用 相同的dom而不是重新創建dom這樣就又可以減少一部分性能開銷 ~~~ ![](https://img.kancloud.cn/df/75/df757beff86ea4bdc8e2997d5225b323_599x280.png) >[danger] ##### 情況分析 ~~~ 1.oldStartVnode / newStartVnode (舊開始節點 / 新開始節點) 2.oldEndVnode / newEndVnode (舊結束節點 / 新結束節點) 3.oldStartVnode / oldEndVnode (舊開始節點 / 新結束節點) 4.oldEndVnode / newStartVnode (舊結束節點 / 新開始節點) 5.前四種比較都不同,則使用新開始節點的key值在【舊開始節點-舊結束節點】的子數組中找相同key的節點。 若沒有在子數組中找到相同key節點則新開始節點是新節點,創建一個該節點的真實DOM節點插入到舊開始節 點指向的真實DOM節點之前;若找到相同key的節點獲取該舊節點,比較新、舊節點的選擇器是否相同, 選擇器不同則創建一個該節點的真實DOM節點插入到舊開始節點指向的真實DOM節點之前,選擇器相同則 對比兩個節點的差異執行相應的操作(如更新DOM節點),并將子數組中key節點指向到真實DOM節點移動 到到舊開始索引指向的真實DOM節點之前(移動的是真實DOM而不是虛擬DOM,虛擬DOM無變化)。 最后將新開始索引指向下一個節點 ~~~ [參考視頻動畫鏈接](https://www.bilibili.com/video/BV1b5411V7i3?t=479) ![](https://img.kancloud.cn/c8/ac/c8ac0f422e27222abd614252e184c2f6_2600x4182.png) >[danger] ##### 代碼實現部分 ~~~ function updateChildren (parentElm: Node, oldCh: VNode[], newCh: VNode[], insertedVnodeQueue: VNodeQueue) { 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: KeyToIndexMap | undefined let idxInOld: number let elmToMove: VNode let before: any while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { // 索引變化后,可能會把節點設置為空 if (oldStartVnode == null) { // 節點為空移動索引 oldStartVnode = oldCh[++oldStartIdx] // Vnode might have been moved left } else if (oldEndVnode == null) { oldEndVnode = oldCh[--oldEndIdx] } else if (newStartVnode == null) { newStartVnode = newCh[++newStartIdx] } else if (newEndVnode == null) { newEndVnode = newCh[--newEndIdx] // 比較開始和結束節點的四種情況 } else if (sameVnode(oldStartVnode, newStartVnode)) { // 1. 比較老開始節點和新開始節點 patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue) oldStartVnode = oldCh[++oldStartIdx] newStartVnode = newCh[++newStartIdx] } else if (sameVnode(oldEndVnode, newEndVnode)) { // 2.比較老結束節點和新的結束節點 patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue) oldEndVnode = oldCh[--oldEndIdx] newEndVnode = newCh[--newEndIdx] } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right // 3.比較老開始節點和新的結束節點 patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue) api.insertBefore(parentElm, oldStartVnode.elm!, api.nextSibling(oldEndVnode.elm!)) oldStartVnode = oldCh[++oldStartIdx] newEndVnode = newCh[--newEndIdx] } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left // 4.比較老結束節點和新的開始節點 patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue) api.insertBefore(parentElm, oldEndVnode.elm!, oldStartVnode.elm!) oldEndVnode = oldCh[--oldEndIdx] newStartVnode = newCh[++newStartIdx] } else { // 開始節點和結束節點都不相同 // 使用newStartNode 的key在老節點數組中找相同節點 // 先設置記錄 key 和index對象 if (oldKeyToIdx === undefined) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) } // 遍歷 newStartVnode,從舊節點中找相同key的oldVnode的索引 idxInOld = oldKeyToIdx[newStartVnode.key as string] // 如果是新的vnode if (isUndef(idxInOld)) { // New element // 如果沒找到,newStartNode 是新節點 // 創建元素插入DOM樹 api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm!) } else { // 如果找到相同key的老節點,記錄到elmToMove遍歷 elmToMove = oldCh[idxInOld] if (elmToMove.sel !== newStartVnode.sel) { // 如果新舊節點的選擇器不同 // 創建新開始節點對應的DOM元素,插入到DOM樹種 api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm!) } else { // 如果相同,patchVnode() // 把elmToMove 對應的DOM元素,移動到左邊 patchVnode(elmToMove, newStartVnode, insertedVnodeQueue) oldCh[idxInOld] = undefined as any api.insertBefore(parentElm, elmToMove.elm!, oldStartVnode.elm!) } } // 重新給newStartVnode 復制,指向下一個新節點 newStartVnode = newCh[++newStartIdx] } } // 循環結束,老節點數組先遍歷完成或者新節點數組先遍歷完成 if (oldStartIdx <= oldEndIdx || newStartIdx <= newEndIdx) { if (oldStartIdx > oldEndIdx) { // 如果老節點數組先遍歷完成,說明有新的節點剩余 // 把剩余的新節點都插入到右邊 before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue) } else { // 如果新節點數組先遍歷完成,說明老節點有剩余 removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) } } } ~~~ >[danger] ##### 虛擬dom中key的好處 ~~~ Key 是用來優化 Diff 算法的。Diff算法核心在于同層次節點比較,Key 就是用于在比較同層次新、舊節點時, 判斷其是否相同。 Key 一般用于生成一列同類型節點時使用,這種情況下,當修改這些同類型節點的某個內容、變更位置、刪除、 添加等時,此時界面需要更新視圖,Vue 會調用 patch 方法通過對比新、舊節點的變化來更新視圖。 其從根節點開始若新、舊 VNode 相同,則調用 patchVnode patchVnode 中若新節點沒有文本,且新節點和舊節點都有有子節點,則需對子節點進行 Diff 操作,即調用 updateChildren,Key 就在 updateChildren 起了大作用 updateChildren 中會遍歷對比上步中的新、舊節點的子節點,并按 Diff 算法通過 sameVnode 來判斷要對比 的節點是否相同 若這里的子節點未設置 Key,則此時的每個新、舊子節點在執行 sameVnode 時會判定相同,然后再次執行一次 patchVnode 來對比這些子節點的子節點 若設置了 Key,當執行 sameVnode 若 Key 不同 sameVnode 返回 false,然后執行后續判斷; 若 Key 相同 sameVnode 返回 true,然后再執行 patchVnode 來對比這些子節點的子節點 即,使用了 Key 后,可以優化新、舊節點的對比判斷,減少了遍歷子節點的層次,少使用很多次 patchVnode ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看