1. 程式人生 > >從Vue的DOM構建機制中理解key

從Vue的DOM構建機制中理解key

最近在學vue,看完vue的基礎部分後大部分的知識都很容易理解,唯獨頻頻提到的key讓我疑惑了很久~

官方文件上對key的第一次描述是:

如果只是單純的看文件,理解的可能不是太清楚,所以我專門寫了上面這個demo來驗證文件所說的:

先看效果:普通的for迴圈(沒加key)
     加了key值得for迴圈

可以發現在普通的v-for迴圈中,在我點選刪除後,第一個被選中的input並沒有一併刪掉,而是簡單的將資料項進項了移動,也就是說它的DOM元素並沒有刪掉。回頭看官網文件描述的v-for的“就地複用”——Vue 將不會移動 DOM 元素來匹配資料項的順序, 而是簡單複用此處每個元素,並且確保它在特定索引下顯示已被渲染過的每個元素。
沒錯vue在更新已渲染好的dom時只是將你兩個大括號裡的資料項進行了移動但dom元素的順序並沒變。那麼第二個效果中為什麼被選中的input又可以被刪掉呢?其實如果只是單純的看這句話,你可能會很迷惑,什麼是跟蹤節點的身份?key在實際應用中又有什麼作用呢?好現在我們來揭開它的‘面紗’:
要清楚的瞭解key,必須先了解vue的虛擬DOM的Diff演算法(相關連結:那什麼是虛擬dom?字面意思就是“非真實”的dom。為什麼要用這個“非真實”的dom?因為我們實際建立的dom會有很多東西,就那我們最常見的div來說:
var t_div = document.createElement('div');     //js建立一個div
console.log(typeof t_div);  
for(item in t_div){
    console.log(item)   //輸出所建立div物件的所有內容
}


可以看到一個“真實的”dom元素是包含很多內容的,如果vue每次都用“真實的”dom元素,則專案的效能會大大減弱。所以我們可以把虛擬dom理解成用一個含有真實元素重要引數、較為簡單的物件去替換一個複雜物件。而且當dom元素改變時會先和虛擬dom進行比較,看有哪些引數改變了,如果對比後部分引數的資料確實需要改變,才會去改變真實don元素。你可能會去想如果改變元素為什麼不直接修改真實的dom,的確有很多時候直接修改真實dom效果更好,但你的專案非常大時,有許多的頁面要維護,你的夥伴可能不會去維護,這時候虛擬dom它的存在是很有意義的,它雖然有時候不是最好的,但它有絕對的普適性,它主要是維持效能和後期維護之間的平衡。其實你可以把它看作是一箇中間層,就想java編譯時需要的那個虛擬機器的作用類似,js通過這個中間層來進行UI渲染。


在diff演算法中,他只會對同級的dom元素進行比較,例如:

<div class="firstFloor">
    <p>這是第二級
        <span>這是第三級</span>
    </p>
</div>

改變後:

<div class="firstFloor">
    <p>這是第二級</p>
    <span>變成了第二級</span>
</div>

這個時候最好的解決方法是將原來的<span>標籤移動到改變後的位置,但diff演算法不這麼做,它首先會比較每一層的變化,就如上面的例子,他的做法是把第三層的<span>標籤先刪掉,再在第二層裡建立一個<span>.那麼在同層比較中:“如果節點型別不同,直接幹掉前面的節點,再建立並插入新的節點,不會再比較這個節點以後的子節點了。如果節點型別相同,則會重新設定該節點的屬性,從而實現節點的更新”(引用知乎說法)。

說了這麼多,可以告訴你key到底是幹嘛的用的了!我們希望可以在B和C之間加一個F,Diff演算法預設執行起來是這樣的:

即把C更新成F,D更新成C,E更新成D,最後再插入E,是不是很沒有效率?所以我們需要使用key來給每個節點做一個唯一標識,Diff演算法就可以正確的識別此節點,找到正確的位置區插入新的節點。

之前說diff演算法大多時候效果不怎麼好,那key正是來解決這個問題的,所以很直觀,key的作用大大提高了虛擬DOM的更新速度。另外vue中在使用相同標籤名元素的過渡切換時,也會使用到key屬性,其目的也是為了讓vue可以區分它們,否則vue只會替換其內部屬性而不會觸發過渡效果。