v-for中的key是什么作用?
在使用v-for進行列表渲染時,我們通常會給元素或者組件綁定一個key屬性。
這個key屬性有什么作用呢?我們先來看一下官方的解釋:
- key屬性主要用在Vue的虛擬DOM算法,在新舊nodes對比時辨識VNodes;
- 如果不使用key,Vue會使用一種最大限度減少動態元素並且盡可能的嘗試就地修改/復用相同類型元素的算法;
- 而使用key時,它會基於key的變化重新排列元素順序,並且會移除/銷毀key不存在的元素;
官方的解釋對於初學者來說並不好理解,比如下面的問題:
- 什么是新舊nodes,什么是VNode?
- 沒有key的時候,如何嘗試修改和復用的?
- 有key的時候,如何基於key重新排列的?
認識VNode
我們先來解釋一下VNode的概念:
- 因為目前我們還沒有比較完整的學習組件的概念,所以目前我們先理解HTML元素創建出來的VNode;
- VNode的全稱是Virtual Node,也就是虛擬節點;
- 事實上,無論是組件還是元素,它們最終在Vue中表示出來的都是一個個VNode(一個個節點);
- VNode的本質是一個JavaScript的對象;
虛擬DOM(VDOM 是多個vnode形成的樹結構)
如果我們不只是一個簡單的div,而是有一大堆的元素,那么它們應該會形成一個VNode Tree:
虛擬DOM的好處:做跨平台,可以做服務端渲染、移動端渲染等。
插入F的案例
我們先來看一個案例:這個案例是當我點擊按鈕時會在中間插入一個f;
我們可以確定的是,這次更新對於ul和button是不需要進行更新,需要更新的是我們li的列表:
- 在Vue中,對於相同父元素的子元素節點並不會重新渲染整個列表;
- 因為對於列表中a、b、c、d它們都是沒有變化的;
- 在操作真實DOM的時候,我們只需要在中間插入一個f的li即可;
那么Vue中對於列表的更新究竟是如何操作的呢?
- Vue事實上會對於有key和沒有key會調用兩個不同的方法;
- 有key,那么就使用patchKeyedChildren方法;
- 沒有key,那么久使用patchUnkeyedChildren方法;
Vue源碼對於key的判斷
沒有key的操作(源碼)
沒有key的過程如下
我們會發現上面的diff算法效率並不高:
- c和d來說它們事實上並不需要有任何的改動;
- 但是因為我們的c被f所使用了,所有后續所有的內容都要一次進行改動,並且最后進行新增;
有key執行操作(源碼)
有key的diff算法如下(一)
第一步的操作是從頭開始進行遍歷、比較:
- a和b是一致的會繼續進行比較;
- c和f因為key不一致,所以就會break跳出循環;
第二步的操作是從尾部開始進行遍歷、比較:
第三步是如果舊節點遍歷完畢,但是依然有新的節點,那么就新增節點:
第四步是如果新的節點遍歷完畢,但是依然有舊的節點,那么就移除舊節點:
第五步是最特色的情況,中間還有很多未知的或者亂序的節點:
所以我們可以發現,Vue在進行diff算法的時候,會盡量利用我們的key來進行優化操作:
- 在沒有key的時候我們的效率是非常低效的;
- 在進行插入或者重置順序的時候,保持相同的key可以讓diff算法更加的高效;