<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [vue3] vue3更新組件流程與diff算法

      Vue3中,組件的更新通過patch函數(shù)進行處理。

      patch函數(shù)

      源碼位置:core/packages/runtime-core/src/renderer.ts at main · vuejs/core (github.com)

      const patch: PatchFn = (
          n1,
          n2,
          container,
          anchor = null,
          parentComponent = null,
          parentSuspense = null,
          namespace = undefined,
          slotScopeIds = null,
          optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren,
      ) => {
          // 二者相同,不需要更新
          if (n1 === n2) {
              return
          }
      
          // vnode類型不同,直接卸載舊節(jié)點
          if (n1 && !isSameVNodeType(n1, n2)) {
              anchor = getNextHostNode(n1)
              unmount(n1, parentComponent, parentSuspense, true)
              n1 = null
          }
      	// ......
      
          const { type, ref, shapeFlag } = n2
          switch (type) {
              case Text:
                  // 處理文字節(jié)點
                  break
              case Comment:
                  // 處理注釋節(jié)點
                  break
              case Static:
                  // 靜態(tài)節(jié)點
                  break
              case Fragment:
                  // Fragment節(jié)點
                  break
              default:
                  if (shapeFlag & ShapeFlags.ELEMENT) {
                      // 處理普通DOM元素
                  } else if (shapeFlag & ShapeFlags.COMPONENT) {
                      // 處理組件
                  } else if (shapeFlag & ShapeFlags.TELEPORT) {
                      // 處理teleport
                  } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
                      // 處理suspense
                  } else if (__DEV__) {
                      // 報錯:vnode類型不在可識別范圍內(nèi)
                      warn('Invalid VNode type:', type, `(${typeof type})`)
                  }
          }
      }
      

      patch函數(shù)用來掛載或者更新vnode

      patch的大致流程:

      1. n1n2如果相等,則表示無變化,直接退出;
      2. n1n2如果引用不同,則先檢查其vnode類型,如果類型不同,則直接卸載n1,掛載n2
      3. 主流程:根據(jù)n1n2的vnode類型,調(diào)用不同的process函數(shù)。

      process

      process函數(shù)的參數(shù)列表大致相同,都是要傳入n1n2container等參數(shù)。patch函數(shù)主要起到一個分類討論的功能。

      這里只討論普通元素類型組件類型的vnode處理過程,因為這是Vue應用中最常見、覆蓋范圍最廣的兩種類型。

      普通元素類型,即ShapeFlags.ELEMENT,在瀏覽器環(huán)境下就是指DOM類型。

      普通元素vs組件

      從組件樹的角度來理解普通元素和組件元素的區(qū)別。

      image-20240807201257321

      一個組件的children可以是普通元素或組件元素。

      • 葉子節(jié)點必須是普通元素,因為只有普通元素能夠通過相關(guān)平臺掛載到界面上。Vue會在編譯時確定mount方法,以適應不同的平臺。對于瀏覽器環(huán)境來說,普通元素是通過vnode來表示DOM節(jié)點,將vnode轉(zhuǎn)換成實際DOM元素并插入到頁面上的操作由vue3源碼中的runtime-dom這個package實現(xiàn)。

        葉子節(jié)點必須是普通元素,但是普通元素不一定是葉子節(jié)點,比如一個div標簽內(nèi)部可以包含其它組件。

      • 葉子節(jié)點不可能是組件,因為組件必須被實現(xiàn)且被注冊,其實現(xiàn)必須使用已注冊的組件或者普通元素。并且組件是虛擬元素,并不能被實際掛載到指定平臺上,只能遞歸地patch它的children,直到把普通元素都掛載到界面上。

      在 Vue3 - patch 函數(shù)的源碼中可以看到除了這兩種類型,還有很多針對其它類型 vnode 的 process 函數(shù),這些 process 函數(shù)主要做的只有兩件事:掛載和更新。

      對于舊vnode n1 和 新vnode n2

      • n1null時,則表示掛載n2
      • n1不為null時,則表示n1更新為n2

      patchElement

      patchElement 會對它的 children 也進行 patch,也就是調(diào)用 patchChildren 函數(shù)。

      children 有三種情況:文本、數(shù)組、NULL。

      image-20240821015027093

      diff算法

      diff 算法用于將舊children的vnode數(shù)組更新為新children的vnode數(shù)組,它通過比較兩個序列,盡可能地復用相同的vnode,以此來減少頻繁創(chuàng)建vnode帶來的開銷。

      事實上,diff 是在 patchKeyedChildren 中實現(xiàn)的,對于沒有設(shè)置 key 的數(shù)組,patchChildren函數(shù)內(nèi)部調(diào)用的是 patchUnkeyedChildren,函數(shù)實現(xiàn)大致如下:

      1. 計算兩個數(shù)組的長度的最小值 commonLength
      2. commonLength 個 vnode 直接 patch 更新,不會考慮移動到不同位置來復用;
      3. 舊序列如果有剩余則unmount,新序列如果有多余則mount。

      這種做法在大多數(shù)情況下都會需要創(chuàng)建 vnode,開銷還是比較大的。因此為了提高渲染性能,使用渲染列表的時候要寫上 key。

      image-20240821124406433

      vue3 的 diff算法實現(xiàn)在patchKeyedChildren函數(shù)中,主要包含五個流程,其中第五個是最復雜的步驟:

      1. 兩個序列從頭部向尾部依次同步,直到不能匹配進入下個流程;

        起始索引都是從0開始,用一個變量 i 就可以了。

        image-20240821153449683
      2. 兩個序列從尾部向頭部依次同步,直到不能匹配進入下個流程;

        兩個序列長度可能不一樣,最后一個元素的索引不一樣,因此需要兩個變量 e1e2 來指向 ending index

        image-20240821153834805

      在上述兩個流程之后:

      1. 如果舊序列遍歷完了,而新序列還有剩余,則新序列剩余的vnode依次mount;

        • i>e1則表示舊序列遍歷完了;
        • i<=e2則表示新序列還有剩余;
        • while(i<=e2){...; i++}把剩余的vnode都掛載。
        image-20240821154722336
      2. 如果新序列遍歷完了,而舊序列還有剩余,則舊序列剩余的vnode依次unmount;

        • i>e2表示新序列都遍歷完了;
        • i<=e1表示舊序列還有剩余;
        • while(i<=e1){ unmount(...); i++ }將剩余的舊 vnode 都卸載。
        image-20240821170437767
      3. 未知序列,盡可能地通過移動復用vnode,剩下的mount或者unmount。

        頭部和尾部都同步了若干vnode,但是兩個序列都還沒有遍歷完成,說明中間有一段序列是混亂的、難以匹配的。

        image-20240821172910666

        在步驟5中,有細分為多個子步驟:

        首先用s1s2表示舊新序列的起始索引:const s1 = s2 = i;

        5.1. 遍歷新序列,使用Map建立keynewIndex的映射:keyToNewIndexMap

        使用Map的原因是PropertyKey這個類型是聯(lián)合類型string | number | symbol,不能簡單的用對象或數(shù)組表示。

        建立 key 到 index 的映射,是為了后續(xù)我們可以通過舊序列中的 key 來建立可復用情況下新舊節(jié)點之間的映射關(guān)系。

        // 這段代碼不是源碼,只保留主干。
        const keyToNewIndexMap: Map<PropertyKey, number> = new Map()
        for (i = s2; i <= e2; i++) {
            const nextChild = c2[i];	// c2 即 children2
            if (nextChild.key != null) 
                keyToNewIndexMap.set(nextChild.key, i)
            }
        }
        

        5.2. 遍歷舊序列,使用一個newIndexToOldIndexMap數(shù)組建立新舊序列中可復用節(jié)點的位置對應關(guān)系。

        newIndexToOldIndexMap的作用

        通過這個數(shù)組,我們可以知道一個新vnode可以由哪個舊vnode更新得到。在這個數(shù)組中,newIndex是以 0 開始的,而 oldIndex 是以 1 開始的,這是為了把 oldIndex==0 作為一個特殊標識,表示新節(jié)點在舊序列中不存在。當newIndexToOldIndexMap[k] = 0,則表示新序列中第 k 個vnode在舊序列中不存在,無法復用。

        image-20240821202906314

        newIndexToOldIndexMap的構(gòu)建過程

        遍歷舊序列 c1

        for(let i=s1; i<=e1; i++){...}
        
        • 使用 keyToNewIndexMap 查詢 c1[i] 的 key:

          • 如果 key 為 undefined,則說明這個舊的 vnode 在新序列中不存在了,卸載這個舊 vnode;

          • 如果 key 為某個數(shù)字,則表明這個舊 vnode 在新序列中有 vnode 的 key 跟它一樣,可以復用。使用 patch 函數(shù)將舊節(jié)點更新為新節(jié)點。

            這一步驟中記錄新舊序列索引映射的代碼是 newIndexToOldIndexMap[newIndex - s2] = i + 1

            • 減去s2是因為序列包含 diff 算法步驟1同步的頭節(jié)點;
            • i+1是因為這個數(shù)組記錄的 oldIndex 是從 1 開始的。
        • 如果發(fā)現(xiàn)新序列中的節(jié)點都找到與之對應的舊節(jié)點了,那么 for 循環(huán)后續(xù)的舊節(jié)點都直接卸載。

        在這個步驟中,還通過一個 moved 變量來記錄節(jié)點的相對位置是否被移動了:

        if (newIndex >= maxNewIndexSoFar) {
            maxNewIndexSoFar = newIndex
        } else {
            moved = true
        }
        

        如果當前新節(jié)點的索引 newIndex 大于或等于此前遍歷到的最大新節(jié)點索引 maxNewIndexSoFar,那么當前節(jié)點在新列表中的順序相對于舊列表來說是保持遞增的。

        image-20240821211605198

      ? 5.3 移動與掛載

      ? 經(jīng)過上面若干步驟,能復用的舊節(jié)點都通過 patch 將數(shù)據(jù)更新到新節(jié)點上了,不能復用的舊節(jié)點都被卸載了。

      ? 而新節(jié)點如果沒有在舊序列中出現(xiàn),則掛載;如果在舊序列中出現(xiàn)了,

      • 如果moved為 false,則表示(已經(jīng)執(zhí)行復用操作的)新節(jié)點的順序和在舊序列中的相對順序是一致的,這種情況無需處理;

      • 如果moved為 true,則表示相對順序不一致,需要移動 vnode。

        為了減少移動次數(shù),這里應用了最長遞增子序列算法,計算了數(shù)組newIndexToOldIndexMap的最長遞增子序列。

        從上圖右邊的子圖中可以看出,遞增子序列越長意味著相對順序一致的子序列越長,那么需要移動的 vnode 就越少。

        思考版圖大致如下:

        image-20240821232108564

      posted @ 2024-08-21 23:31  feixianxing  閱讀(234)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产播放91色在线观看| 一本加勒比hezyo无码人妻| 欧美黑人巨大videos精品| 激情人妻中出中文字幕一区| 亚洲日本VA中文字幕在线| 欧美日韩一区二区三区视频播放| 欧美交a欧美精品喷水| 国产成人精品久久性色av| 国产一区二区三中文字幕| 亚洲精品成人一二三专区| 少妇宾馆粉嫩10p| 国产午夜精品亚洲精品国产| 人妻在线中文字幕| 午夜福利日本一区二区无码| 国产中文字幕精品喷潮| 九九热精品免费视频| 欧美黑人巨大videos精品| 国产精品自拍午夜福利| 蜜桃视频无码区在线观看| 精品无码国产不卡在线观看| 五月婷婷激情视频俺也去淫| 亚洲色大成网站WWW永久麻豆| 97精品伊人久久久大香线蕉| 亚洲最大的成人网站| 亚洲精品无码成人A片九色播放| 亚洲人成网7777777国产| 最新午夜男女福利片视频| 亚洲精品理论电影在线观看| 亚洲精品日韩在线丰满| jizz国产免费观看| 亚洲日韩国产二区无码| 国产精品午夜精品福利| 2019亚洲午夜无码天堂| 国产爆乳无码视频在线观看3| 中文字幕国产精品专区| 福利一区二区在线视频| 国产色视频一区二区三区| 狠狠躁夜夜躁人人爽天天5| 小污女小欲女导航| 国产精品成人午夜福利| 久热这里有精品视频在线|