中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue Diff算法怎么掌握

發布時間:2022-10-10 13:54:00 來源:億速云 閱讀:210 作者:iii 欄目:編程語言

這篇“Vue Diff算法怎么掌握”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Vue Diff算法怎么掌握”文章吧。

為什么需要diff算法?

對于一個容器(比如我們常用的#app)而言,它的內容一般有三種情況:

  • 字符串類型,即是文本。

  • 子節點數組,即含有一個或者多個子節點

  • null,即沒有子節點

在vue中,會將dom元素當作vdom進行處理,我們的HTML Attributes、事件綁定都會現在vdom上進行操作處理,最終渲染成真實dom。

Virtual Dom:用于描述真實dom節點的JavaScript對象。

使用vdom的原因在于,如果每次操作都是直接對真實dom進行操作,那么會造成很大的開銷。使用vdom時就能將性能消耗從真實dom操作的級別降低至JavaScript層面,相對而言更加優秀。 一個簡單的vdom如下:

const vdom = {
    type:"div",
    props:{
        class: "class",
        onClick: () => { console.log("click") }
    },
    children: [] // 簡單理解這就是上述三種內容
}

對于vue節點的更新而言,是采用的vdom進行比較。

diff算法便是用于容器內容的第二種情況。當更新前的容器中的內容是一組子節點時,且更新后的內容仍是一組節點。如果不采用diff算法,那么最簡單的操作就是將之前的dom全部卸載,再將當前的新節點全部掛載。

但是直接操作dom對象是非常耗費性能的,所以diff算法的作用就是找出兩組vdom節點之間的差異,并盡可能的復用dom節點,使得能用最小的性能消耗完成更新操作。

接下來說三個diff算法,從簡單到復雜循序漸進。

簡單的Diff算法

為什么需要key?

接下來通過兩種情況進行說明為什么需要key?

如果存在如下兩組新舊節點數組:

const oldChildren = [
    {type: 'p'},
    {type: 'span'},
    {type: 'div'},
]
const newChildren = [
    {type: 'div'},
    {type: 'p'},
    {type: 'span'},
    {type: 'footer'},
]

如果我們是進行正常的比較,步驟應該是這樣:

找到相對而言較短的一組進行循環對比

  • 第一個p標簽與div標簽不符,需要先將p標簽卸載,再將div標簽掛載。

  • 第一個spam標簽與p標簽不符,需要先將span標簽卸載,再將p標簽掛載。

  • 第一個div標簽與span標簽不符,需要先將div標簽卸載,再將span標簽掛載。

  • 最后多余一個標簽footer存在在新節點數組中,將其掛載即可。

那么我們發現其中進行了7次dom操作,但是命名前三個都是可以復用的,只是位置發生了變化。如果進行復用節點我們需要判斷兩個節點是相等的,但是現在的已有條件還不能滿足。

所以我們需要引入key,它相當于是虛擬節點的身份證號,只要兩個虛擬節點的type和key都相同,我們便認為他們是相等的,可以進行dom的復用。

這時我們便可以找到復用的元素進行dom的移動,相對而言會比不斷的執行節點的掛載卸載要好。

但是,dom的復用不意味不需要更新:

const oldVNode = {type: 'p', children: 'old', key: 1}
const newVNode = {type: 'p', children: 'new', key: 2}

上述節點擁有相同的type和key,我們可以復用,此時進行子節點的更新即可。

簡單的diff算法步驟

先用一個例子說明整個流程,再敘述其方法

const oldChildren = [
    {type: 'p', children: 'p', key: 1},
    {type: 'span', children: 'span', key: 2},
    {type: 'div', children: 'div', key: 3},
    {type: 'section', children: 'section', key : 4},
]
const newChildren = [
    {type: 'div', children: 'new div', key: 3},
    {type: 'p', children: 'p', key: 1},
    {type: 'span', children: 'span', key: 2},
    {type: 'footer', children: 'footer', key: 5},
]

為了敘述簡單,這里使用不同的標簽。整個流程如下:

Vue Diff算法怎么掌握

  • 從新節點數組開始遍歷

  • 第一個是div標簽,當前的下標是0,之前的下標是2。相對位置并未改變,不需要移動,只需要就行更新節點內容即可。

  • 第二個是p標簽,當前的下標是1,之前的下標是0。就相對位置而言,p相對于div標簽有變化,需要進行移動。移動的位置就是在div標簽之后。

  • 第三個是span標簽,當前的下標是2,之前的下標是1。就相對位置而言,p相對于div標簽有變化,需要進行移動。移動的位置就是在p標簽之后。

  • 第四個標簽是footer,遍歷舊節點數組發現并無匹配的元素。代表當前的元素是新節點,將其插入,插入的位置是span標簽之后。

  • 最后一步,遍歷舊節點數組,并去新節點數組中查找是否有對應的節點,沒有則卸載當前的元素。

如何找到需要移動的元素?

上述聲明了一個lastIdx變量,其初始值為0。作用是保存在新節點數組中,對于已經遍歷了的新節點在舊節點數組的最大的下標。那么對于后續的新節點來說,只要它在舊節點數組中的下標的值小于當前的lastIdx,代表當前的節點相對位置發生了改變,則需要移動,

舉個例子:div在舊節點數組中的位置為2,大于當前的lastIdx,更新其值為2。對于span標簽,它的舊節點數組位置為1,其值更小。又因為當前在新節點數組中處于div標簽之后,就是相對位置發生了變化,便需要移動。

當然,lastIdx需要動態維護。

總結

簡單diff算法便是拿新節點數組中的節點去舊節點數組中查找,通過key來判斷是否可以復用。并記錄當前的lastIdx,以此來判斷節點間的相對位置是否發生變化,如果變化,需要進行移動。

雙端diff算法

簡單diff算法并不是最優秀的,它是通過雙重循環來遍歷找到相同key的節點。舉個例子:

const oldChildren = [
    {type: 'p', children: 'p', key: 1},
    {type: 'span', children: 'span', key: 2},
    {type: 'div', children: 'div', key: 3},
]
const newChildren = [
    {type: 'div', children: 'new div', key: 3},
    {type: 'p', children: 'p', key: 1},
    {type: 'span', children: 'span', key: 2},
]

其實不難發現,我們只需要將div標簽節點移動即可,即進行一次移動。不需要重復移動前兩個標簽也就是p、span標簽。而簡單diff算法的比較策略即是從頭至尾的循環比較策略,具有一定的缺陷。

顧名思義,雙端diff算法是一種同時對新舊兩組子節點的兩個端點進行比較的算法

Vue Diff算法怎么掌握

那么雙端diff算法開始的步驟如下:

  • 比較 oldStartIdx節點 與 newStartIdx 節點,相同則復用并更新,否則

  • 比較 oldEndIdx節點 與 newEndIdx 節點,相同則復用并更新,否則

  • 比較 oldStartIdx節點 與 newEndIdx 節點,相同則復用并更新,否則

  • 比較 oldEndIdx節點 與 newStartIdx 節點,相同則復用并更新,否則

簡單概括:

  • 舊頭 === 新頭?復用,不需移動

  • 舊尾 === 新尾?復用,不需移動

  • 舊頭 === 新尾?復用,需要移動

  • 舊尾 === 新頭?復用,需要移動

對于上述例子而言,比較步驟如下:

Vue Diff算法怎么掌握

上述的情況是一種非常理想的情況,我們可以根據現有的diff算法完全的處理兩組節點,因為每一輪的雙端比較都會命中其中一種情況使得其可以完成處理。

但往往會有其他的情況,比如下面這個例子:

const oldChildren = [
    {type: 'p', children: 'p', key: 1},
    {type: 'span', children: 'span', key: 2},
    {type: 'div', children: 'div', key: 3},
    {type: 'ul', children: 'ul', key: 4},
]
const newChildren = [
    {type: 'div', children: 'new div', key: 3},
    {type: 'p', children: 'p', key: 1},
    {type: 'ul', children: 'ul', key: 4},
    {type: 'span', children: 'span', key: 2},
]

此時我們會發現,上述的四個步驟都會無法命中任意一步。所以需要額外的步驟進行處理。即是:在四步比較失敗后,找到新頭節點在舊節點中的位置,并進行移動即可。動圖示意如下:

Vue Diff算法怎么掌握

當然還有刪除、增加等均不滿足上述例子的操作,但操作核心一致,這里便不再贅述。

總結

雙端diff算法的優勢在于對于一些比較特殊的情況能更快的對節點進行處理,也更貼合實際開發。而雙端的含義便在于通過兩組子節點的頭尾分別進行比較并更新。

快速diff算法

首先,快速diff算法包含了預處理步驟。它借鑒了純文本diff的思路,這時它為何快的原因之一。

比如:

const text1 = '我是快速diff算法'
const text2 =  '我是雙端diff算法'

那么就會先從頭比較并去除可用元素,其次會重后比較相同元素并復用,那么結果就會如下:

const text1 = '快速'
const text2 = '雙端'

此時再進行一些其他的比較和處理便會簡單很多。

其次,快速diff算法還使用了一種算法來盡可能的復用dom節點,這個便是最長遞增子序列算法。為什么要用呢?先舉個例子:

// oldVNodes
const vnodes1 = [
    {type:'p', children: 'p1', key: 1},
    {type:'div', children: 'div', key: 2},
    {type:'span', children: 'span', key: 3},
    {type:'input', children: 'input', key: 4},
    {type:'a', children: 'a', key: 6}
    {type:'p', children: 'p2', key: 5},
]
// newVNodes 
const vnodes2 = [
    {type:'p', children: 'p1', key: 1},
    {type:'span', children: 'span', key: 3},
    {type:'div', children: 'div', key: 2},
    {type:'input', children: 'input', key: 4},
    {type:'p', children: 'p2', key: 5},
]

經過預處理步驟之后得到的節點如下:

// oldVNodes
const vnodes1 = [
    {type:'div', children: 'div', key: 2},
    {type:'span', children: 'span', key: 3},
    {type:'input', children: 'input', key: 4},
    {type:'a', children: 'a', key: 6},
]
// newVNodes 
const vnodes2 = [
    {type:'span', children: 'span', key: 3},
    {type:'div', children: 'div', key: 2},
    {type:'input', children: 'input', key: 4},
]

此時我們需要獲得newVNodes節點相對應oldVNodes節點中的下標位置,我們可以采用一個source數組,先循環遍歷一次newVNodes,得到他們的key,再循環遍歷一次oldVNodes,獲取對應的下標關系,如下:

const source = new Array(restArr.length).fill(-1)
// 處理后
source = [1, 2, 0, -1]

注意!這里的下標并不是完全正確!因為這是預處理后的下標,并不是剛開始的對應的下標值。此處僅是方便講解。 其次,source數組的長度是剩余的newVNodes的長度,若在處理完之后它的值仍然是-1則說明當前的key對應的節點在舊節點數組中沒有,即是新增的節點。

此時我們便可以通過source求得最長的遞增子序列的值為 [1, 2] 。對于index為1,2的兩個節點來說,他們的相對位置在原oldVNodes中是沒有變化的,那么便不需要移動他們,只需要移動其余的元素。這樣便能達到最大復用dom的效果。

步驟

以上述例子來說:

  • 首先進行預處理

Vue Diff算法怎么掌握

注意!預處理過的節點雖然復用,但仍然需要進行更新。

  • 進行source填充

Vue Diff算法怎么掌握

當然這里遞增子序列 [1, 2] 和 [0, 1]都是可以的。

  • 進行節點移動

用索引i指向新節點數組中的最后一個元素

用索引s指向最長遞增子序列中的最后一個元素

然后循環進行以下步驟比較:

source[i] === -1?等于代表新節點,掛載即可。隨后移動i

i === 遞增數組[s]? 等于代表當前的節點存在在遞增子序列中,是復用的節點,當前的節點無需移動。

上述均不成立代表需要移動節點。

Vue Diff算法怎么掌握

節點更新,結束。

以上就是關于“Vue Diff算法怎么掌握”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

德安县| 新野县| 宾川县| 新密市| 延安市| 海兴县| 红桥区| 东山县| 嘉峪关市| 峡江县| 南木林县| 夏津县| 眉山市| 六安市| 建始县| 南投县| 廊坊市| 政和县| 五河县| 防城港市| 定结县| 革吉县| 息烽县| 民勤县| 肇源县| 台东县| 靖远县| 滦平县| 宁明县| 山东省| 依安县| 汉阴县| 三门县| 冕宁县| 尚志市| 定西市| 罗山县| 岗巴县| 高青县| 宿迁市| 红安县|