詳解vue中v-for的key唯一性

1. DOM Diff

要想真正瞭解 key 屬性的存在意義,還真得從 DOM Diff 說起,並不需要深入瞭解 DOM Diff 的原理,而是僅僅需要知道 DOM Diff 的工作過程即可。

Vue 和 React 都采用瞭運用虛擬 DOM 的方式減少瀏覽器不必要的渲染。由於 Vue 和 React 采用的都是 v = render( m ) 的方式渲染視圖的,當 model 數據發生變化時,視圖更新的方式就是重新 render DOM 元素。但是有時候我們隻是改變瞭一個組件中的某一個 div 中的數據,如果采用原生 render 的方式去更新視圖的話,那整個組件都要更新。豈不浪費時間?

我們日常生活中碰到這樣的情況可不會全部更新,就像一個拼圖拼好瞭,後來其中一小塊需要更換,我們找到拿一塊直接替換就好瞭,絕不會說再從頭拼一次。Vue 和 React 的開發者也是這樣想的,就去想方設法優化。

我們人眼一眼就可以看出改變前和改變後的不同之處,隻更新不同之處就可以瞭。但計算機可一眼看不出來,它必須從頭一塊快地對比,直至找到不同之處進行更新。這個將改變前和改變後進行對比找不同的過程就是 DOM Diff,DOM Diff 中的 DOM 是虛擬 DOM,也就是 JavaScript 對象,一一比較找到不同之處後,就去局部更新真正的 DOM。

在比較的過程中, 虛擬 DOM 也會構成一棵虛擬 DOM 樹,DOM Diff 的工作過程就是比較兩棵虛擬 DOM 樹上的對象節點,具體就是每一層和每一層的對應位置進行比較。正是因為計算機隻會比較每一層對應位置的的兩個虛擬 DOM 元素,如果這兩棵樹中改變後的樹的某一層隻是插入瞭一個節點,那樹的結構是不變的,DOM Diff 在比較這一層的時候就會導致錯位比較瞭,如下圖所示:

因為這一層的虛擬 DOM 節點對於 Vue 和 React 來說除瞭 DOM 節點本身外是完全沒有任何不同的,所以 DOM Diff 在比較的時候就隻能按照對應位置一一比較瞭。

一一比較後,如果節點類型相同,那麼就會復用該節點,單單局部更新該節點內不同的內容處。就像上述圖中的,如果這是 ul 下的 li 的虛擬 DOM 節點的話,那一一比較後發現節點類型相同,就復用之前的節點,將節點裡面的內容進行改變,也就是,將C更新成F,D更新成C,E更新成D,最後再插入E。

上述是插入節點的情況,帶來的後果就是效率上的降低,但如果是刪除節點的情況,那帶來的後果可就不僅僅是效率瞭。

假如是點擊一個按鈕刪除一個 li 元素,那新舊虛擬 DOM 樹進行比較的時候,還是根據樹中每一層的對應位置一一比較,比如刪除後的 [1,2,3] 變成瞭 [1,3],它就會將第一個 li 和第二個 li 相比較,發現元素類型沒有變化,就會復用第一個 li,再遞歸對比 li 裡面的,發現都沒變化就繼續復用。到瞭第二個 li 之間比較的時候,發現也都是 li 元素,那就會復用之前的li,單單將 2 變成瞭 3。

此時,如果復用的 li 中有子元素的話,子元素依賴的數據沒有發生變化的話,就會繼續復用之前的子組件,這樣就會導致一個錯位,如下圖:

2. 為同一層的相同類型的元素添加 key 屬性

在上述的 DOM Diff 算法中,比較的僅僅是兩棵樹同一層的對應位置,在不同層之前的元素之間是不需要比較的,而且,當 DOM Diff 的過程中發現,改變後的虛擬 DOM 和之前的虛擬 DOM 類型不同的時候,就會將之前的卸載,重新再添加改變後的元素節點。因此,上述的問題就出現在,兩棵樹中同一層的節點類型相同時,在該層添加或刪除時會降低效率或者帶來 bug。

這就是我們在 v-for 循環中生成同種類型的標簽元素時的情況,如果不為該標簽節點做點什麼,就存在bug隱患,那麼應該做什麼呢?

答案就是為同一層的相同節點類型的節點添加一個唯一標識的 key 值,這樣,在 DOM Diff 進行配對比較時,就會將 key 相同的兩個虛擬 DOM 進行比較,而不是僅僅按照對應位置進行比較瞭。

這樣一來就不會導致錯位比較瞭,就大大提高瞭比較的效率,解決瞭 bug 隱患。

3. key 不能是 index 下標值

因為數組或對象的 index 下標值是唯一的,因此我們經常使用 index 作為 key 屬性的值,有的人說這樣是可以的,會帶來性能上的優化什麼的,但使用 index 下標值是會有大大的 bug 隱患的。

這些 bug 會在你 v-for 循環的數組或對象發生添加或刪除或順序改變時。

那麼為什麼不能使用 index 下標呢?

其實就是因為 index 下標使用瞭跟沒使用瞭一樣,因為在添加和刪除時,某一個特定元素的 index 是會變的,比如 [1,2,3] 變成瞭 [1,3] 後,原來數據 3 對應的下標為2,刪除後數據 3 的下標變成瞭 1,這在 DOM Diff 的時候,會根據 key 值相等的進行兩兩配對比較,這數據3對應的節點前後還是對應不上,因此,使用瞭 index 作為 key 跟沒設置 key 是一樣的效果。

這就是為什麼不要使用 index 作為 key 的原因。

因此:key 屬性值必須是獨一無二的且不會改變的

以上就是詳解vue中v-for的key唯一性的詳細內容,更多關於vue中v-for的key唯一性的資料請關註WalkonNet其它相關文章!

推薦閱讀: