最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談Vue?DIFF

 更新時間:2023年05月06日 08:53:36   作者:陽樹陽樹  
本文主要介紹了淺談Vue?DIFF,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

不帶 key 的操作

對于不帶 key 的情況,我們的操作略顯原始,整個流程如下:

  • 首先獲取 oldChildren 和 newChildren 的長度
  • 如果說兩者一樣長的話

就從 tag/children 逐個進行比對,如果相同,則進行保留,如果不同,則進行刪除,并且重新進行掛載。

  • 如果說兩者不一樣長的話

如果 newChildren 更長,就在進行如上操作之后,再從 newChildren 多處的部分開始,逐個進行添加。

如果 oldChildren 更長,就在進行如上操作之后,再從 oldChildren 多出的部分開始,逐個進行刪除。

代碼如下:

const patch = (n1, n2) => {
    if(n1.tag !== n2.tag) {
      const n1ElParent = n1.el.parentElement;
      n1ElParent.removeChild(n1.el);
      mount(n2, n1ElParent)
    } else {
      // 取出 element 對象,并且在 n2 中進行保存
      const el = n1.el = n2.el
      // 為了獲取 value
      const oldProps = n1.props || []
      const newProps = n2.props || []
      // 遍歷新對象
      for(const key in newProps) {
        const oldValue = oldProps[key]
        const newValue = newProps[key]
        if(oldValue != newValue) {
          if(key.startsWith("on")) {
            el.addEventListener(key.slice(2).toLowerCase(), newValue);
          } else {
            el.setAttribute(key, newValue);
          }
        }
      }
      // 刪除舊的 props
      for(const key in oldProps) {
        if(key.startsWith("on")) {
          const value = oldProps[key];
          // removeEventListener(type, listener)
          // type ==> 一個字符串,表示需要移除的事件類型 | listener ==> 需要從目標(biāo)事件中移除的事件處理函數(shù)。
          el.removeEventListener(key.slice(2).toLowerCase(), value)
        } 
        if(!(key in newProps)) {
          el.removeAttribute(key)
        }
      }
      // 3. 處理children
      const oldChildren = n1.children || []
      const newChildren = n2.children || []
      // 如果新的 node 的 children 只是一個字符串,那直接覆蓋就可以了
      if (typeof newChildren === 'string') {
        // 如果舊的也是字符串,那就判斷一下是不是一樣的,然后覆蓋一下
        if (typeof oldChildren === 'string') {
          if (newChildren != oldChildren) {
            el.textContent = newChildren
          }
        } else {
          // 如果新的是很多很多東西,我直接覆蓋就可以了
          el.innerHTML = newChildren
        }
      } else {
        // 這種情況新的 node 并不是字符串,是數(shù)組啥的
        // 然后判斷一下舊的的情況
        // 如果是字符串
        if(typeof oldChildren === 'string') {
          el.innerHTML = ""
          newChildren.forEach((item) => {
            mount(item, el)
          })
        } else {
          // 如果都不是字符串的話
          // 拿到共同長度
          const commonLength = Math.min(oldChildren.length, newChildren.length)
          for(let i=0;i<commonLength;i++) {
            patch(oldChildren[i], newChildren[i])
          }
          // 如果新的節(jié)點的子節(jié)點多一些的話,那就全部掛載上去
          if(newChildren.length > oldChildren.length) {
            newChildren.slice(commonLength).forEach((item) => {
              mount(item, el)
            })
          }
          // 如果舊的節(jié)點的子節(jié)點多一些,那就一個個刪除!
          if(newChildren.length > oldChildren.length) {
            oldChildren.slice(commonLength).forEach((item) => {
              el.removeChild(item)
            })
          }
        }
      }
    }
  }

帶 key 的操作

這個就比較靈性了。

簡單 DIFF

首先我們還是正常進行比對

我們開兩個 for 循環(huán),外層是 newChildren ,內(nèi)層是 oldChildren,如果兩者的 key 相同,我們就可以進行到下一步了 ==> 找到需要進行移動的元素。

Vue是如何找到需要進行移動的元素

我們會創(chuàng)建一個 lastIndex 參數(shù),用來標(biāo)記 key 相同的情況的最大的 Index,大致邏輯是:

if (j < lastIndex) {
  // 進行 patch 操作
} else {
  lastIndex = i
}

所以在我們的 j 小于 lastIndex 的時候,我們就知道,該移動了~

Vue是如何移動元素的

我們知道 j(舊的vnode Index) < lastIndex 的 case 我們要進行移動

我們首先要拿到 newChildren[i - 1](新 vnode 的 i - 1 的值),當(dāng)前新節(jié)點的前一個 vnode,然后進行移動,最后直接 insert 就好了。

Vue是如何進行新增元素的

我們會創(chuàng)建一個 find 參數(shù),用來判斷這個節(jié)點是不是新節(jié)點

如果是舊節(jié)點,我們就走移動邏輯

如果是新節(jié)點,我們就在后面新增,這里有一個問題,就是我加在誰的后面

如果 newChildren[i - 1] 為 null,我們就直接掛載到第一個節(jié)點上

如果 newChildren[i - 1] 不為 null,我們就把新節(jié)點掛載到 newChildren[i - 1] 上

Vue 是如何刪除多余的舊元素的

我們遍歷完新元素之后,還需要遍歷一遍舊元素,如果說當(dāng)前舊元素的 key 在新元素中無對應(yīng),那就摘出來,刪除它。

雙指針 DIFF

流程如下:

  • 首先是 oldStart <- newStart
  • 然后是 oldEnd <- newEnd
  • 然后是 oldEnd <- newStart
  • 最后是 newEnd <- newStart

這樣看起來是沒有問題的,但是如果沒有一個匹配到呢,那是不是就有問題啦?

在這種情況下,我們應(yīng)該用 newStart 的 key 和 oldChildren 中的所有 key 做比較,得到 idx ,如果 idx > 0 ,則我們就做移動,然后把這個位置設(shè)置為 undefined,后面如果碰到了這個位置就直接跳過去。

快速 DIFF

快速 DIFF 做了一個預(yù)處理,把相同的東西提前打補丁處理了。

分別從前往后開循環(huán)以及從后往前開循環(huán),把相同的處理掉,不同的留下來。

如何新增?

image.png

如圖所示,如果我們前后兩輪循環(huán)遍歷完之后,newEnd <= j,就說明有新增,我們找到 oldEnd,while(j >= newEnd)在它的后面一個接一個的掛載進去就可以了。

如何刪除?

image.png

如圖所示,如果我們前后兩輪循環(huán)遍歷完之后,oldEnd <= j,就說明有新增,我們找到 oldEnd,while (j <= oldEnd) 一路刪除上去。

我們剛才看到的兩種都是理想情況,那如果預(yù)處理之后情況依舊復(fù)雜呢?

image.png

function patchKeyedChildren(n1, n2, container) { 
    const newChildren = n2.children
    const oldChildren = n1.children
    // 更新相同的前置節(jié)點
    // 省略部分代碼
    // 更新相同的后置節(jié)點
    // 省略部分代碼
    if (j > oldEnd && j <= newEnd) { 
        // 省略部分代碼 
    } else if (j > newEnd && j <= oldEnd) { 
        // 省略部分代碼 
    } else {
        // 增加 else 分支來處理非理想情況
    }
}

這個時候我們再開一個 source 數(shù)組

source 數(shù)組將用來存儲新的一組子節(jié)點中的節(jié)點在舊的一組子節(jié)點中的位置索引,后面將會使用它計算出一個最長遞增子序列,并用于輔助完成 DOM 移動的操作

由于之前我們要尋找兩個 key 相等的值需要使用兩個 for 循環(huán),這樣就會導(dǎo)致時間復(fù)雜度相當(dāng)?shù)母?,所以我們在這里使用了一個 key-index[新數(shù)組的索引]的映射表,這樣我們只需要做一次便利就可以了,不需要去做嵌套,時間復(fù)雜度從 O(N^2) -> O(N)。

image.png

如何判斷是否移動?

和之前簡單 DIFF 的流程很像,之前我們是找最大索引值,如果索引小的都需要移動,索引更大則更新 lastIndex

我們在講解簡單 Diff 算法時曾提到,如果在遍歷過程中遇到的索引值呈現(xiàn)遞增趨勢,則說明不需要移動節(jié)點,反之則需要。

image.png

所以這里的 moved 為 true 我們就可以移動了。

如何移動元素通過 source 找到最長遞增子序列

通過最長遞增子序列得到一個子序列索引的數(shù)組,最長遞增子序列不需要移動

image.png

然后做處理,對于新節(jié)點,也就是 source 中是 -1 的情況,我們直接新增就好了,如果不是新節(jié)點,我們就判斷兩者是否相同,相同則移動最長遞增子序列中的索引,不同則移動 source 中的索引。

以下是對于索引中兩者不相同的處理。

image.png

到此這篇關(guān)于淺談Vue DIFF的文章就介紹到這了,更多相關(guān)Vue DIFF內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 手把手教你vue實現(xiàn)動態(tài)路由

    手把手教你vue實現(xiàn)動態(tài)路由

    動態(tài)路由可以根據(jù)不同用戶登錄獲取不一樣的路由層級,可隨時調(diào)配路由,下面這篇文章主要給大家介紹了關(guān)于vue實現(xiàn)動態(tài)路由的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-07-07
  • vue中組件的3種使用方式詳解

    vue中組件的3種使用方式詳解

    這篇文章主要給大家介紹了關(guān)于vue中組件的3種使用方式,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用vue具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • VuePress使用Algolia實現(xiàn)全文搜索

    VuePress使用Algolia實現(xiàn)全文搜索

    這篇文章主要為大家介紹了VuePress使用Algolia實現(xiàn)全文搜索示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • vue jsx 使用指南及vue.js 使用jsx語法的方法

    vue jsx 使用指南及vue.js 使用jsx語法的方法

    這篇文章主要介紹了vue jsx 使用指南及vue.js 使用jsx語法的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-11-11
  • vue實現(xiàn)的上傳圖片到數(shù)據(jù)庫并顯示到頁面功能示例

    vue實現(xiàn)的上傳圖片到數(shù)據(jù)庫并顯示到頁面功能示例

    這篇文章主要介紹了vue實現(xiàn)的上傳圖片到數(shù)據(jù)庫并顯示到頁面功能,結(jié)合實例形式分析了基于vue.js的數(shù)據(jù)庫操作及頁面圖片顯示相關(guān)操作技巧,需要的朋友可以參考下
    2018-03-03
  • Vue中的父子組件通訊以及使用sync同步父子組件數(shù)據(jù)

    Vue中的父子組件通訊以及使用sync同步父子組件數(shù)據(jù)

    這篇文章主要介紹了Vue中的父子組件通訊以及使用sync同步父子組件數(shù)據(jù),對vue感興趣的同學(xué),可以參考下
    2021-04-04
  • 詳解在Vue中如何實現(xiàn)表單圖片裁剪與預(yù)覽

    詳解在Vue中如何實現(xiàn)表單圖片裁剪與預(yù)覽

    在前端開發(fā)中,表單提交是一個常見的操作,有時候,我們需要上傳圖片,但是上傳的圖片可能會非常大,這會增加服務(wù)器的負擔(dān),同時也會降低用戶的體驗,因此,我們通常需要對上傳的圖片進行裁剪和預(yù)覽,本文主要給大家介紹如何在Vue中進行表單圖片裁剪與預(yù)覽
    2023-06-06
  • 總結(jié)Vue Element UI使用中遇到的問題

    總結(jié)Vue Element UI使用中遇到的問題

    這篇文章主要介紹了Vue Element UI使用中遇到的問題,對ElementUI感興趣的同學(xué),可以參考下
    2021-05-05
  • vue 實現(xiàn)通過vuex 存儲值 在不同界面使用

    vue 實現(xiàn)通過vuex 存儲值 在不同界面使用

    今天小編就為大家分享一篇vue 實現(xiàn)通過vuex 存儲值 在不同界面使用,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • vue 組件的封裝之基于axios的ajax請求方法

    vue 組件的封裝之基于axios的ajax請求方法

    今天小編就為大家分享一篇vue 組件的封裝之基于axios的ajax請求方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08

最新評論

望江县| 景泰县| 澄迈县| 专栏| 赣榆县| 万盛区| 金塔县| 泸溪县| 巴林左旗| 闸北区| 长沙县| 渭南市| 神农架林区| 舒兰市| 拜城县| 犍为县| 巴青县| 洛阳市| 新化县| 湖州市| 皋兰县| 阜康市| 台南县| 汝南县| 鲁甸县| 中超| 洛阳市| 高邑县| 太仆寺旗| 阿克陶县| 保山市| 修水县| 曲麻莱县| 清远市| 辉南县| 衡阳县| 德令哈市| 景德镇市| 海原县| 长治市| 丹凤县|