vue 之前的版本沒有限制 v-for 中配置 key 現在需要配置不然會報錯
為什么 必須使用key
因為vue組件高度復用,增加Key可以標識組件的唯一性,key的作用主要是為了高效的更新虛擬DOM,后續再原理給大家講解
如何正確使用key
VUE 使用v-for
更新已渲染的元素列表時,默認用就地復用
策略;列表數據修改的時候,他會根據key值去判斷某個值是否修改,如果修改,則重新渲染這一項,否則復用之前的元素;
大多數都會使用index
(即數組的下標)來作為key
,但其實這是不推薦的一種使用方法;
栗子
const list = [ { id: 1, name: 'test1', }, { id: 2, name: 'test2', }, { id: 3, name: 'test3', }, ]
<div v-for="(item, index) in list" :key="index" >{{item.name}}</div>
現在分兩種插入方式給大家講解
1.在最后一條數據后再加一條數據
const list = [ { id: 1, name: 'test1', }, { id: 2, name: 'test2', }, { id: 3, name: 'test3', }, { id: 4, name: '在最后添加的一條數據', }, ]
此時前三條數據直接復用之前的,新渲染最后一條數據,此時用index
作為key
,沒有任何問題;
第二種情況
2、在中間插入一條數據
const list = [ { id: 1, name: 'test1', }, { id: 4, name: '插隊的那條數據', } { id: 2, name: 'test2', }, { id: 3, name: 'test3', }, ]
此時更新渲染數據,通過index
定義的key
去進行前后數據的對比,發現
之前的數據 之后的數據 key: 0 index: 0 name: test1 key: 0 index: 0 name: test1 key: 1 index: 1 name: test2 key: 1 index: 1 name: 插隊的那條數據 key: 2 index: 2 name: test3 key: 2 index: 2 name: test2 key: 3 index: 3 name: test3
通過上面清晰的對比,發現除了第一個數據可以復用之前的之外,另外三條數據都需要重新渲染;
是不是很驚奇,我明明只是插入了一條數據,怎么三條數據都要重新渲染?而我想要的只是新增的那一條數據新渲染出來就行了
最好的辦法是使用數組中不會變化的那一項作為key
值,對應到項目中,即每條數據都有一個唯一的id
,來標識這條數據的唯一性;使用id
作為key
值,我們再來對比一下向中間插入一條數據,此時會怎么去渲染
之前的數據 之后的數據 key: 1 id: 1 index: 0 name: test1 key: 1 id: 1 index: 0 name: test1 key: 2 id: 2 index: 1 name: test2 key: 4 id: 4 index: 1 name: 插隊的那條數據 key: 3 id: 3 index: 2 name: test3 key: 2 id: 2 index: 2 name: test2 key: 3 id: 3 index: 3 name: test3
現在對比發現只有一條數據變化了,就是id
為4的那條數據,因此只要新渲染這一條數據就可以了,其他都是就復用之前的;
下面大致從虛擬DOM的Diff算法實現的角度去解釋一下
vue和react的虛擬DOM的Diff算法大致相同,其核心是基於兩個簡單的假設:
- 兩個相同的組件產生類似的DOM結構,不同的組件產生不同的DOM結構。
- 同一層級的一組節點,他們可以通過唯一的id進行區分。基於以上這兩點假設,使得虛擬DOM的Diff算法的復雜度從O(n^3)降到了O(n)。
引用React’s diff algorithm中的例子:
當某一層有很多相同的節點時,也就是列表節點時,Diff算法的更新過程默認情況下也是遵循以上原則。
比如一下這個情況:
我們希望可以在B和C之間加一個F,Diff算法默認執行起來是這樣的:
即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很沒有效率?
所以我們需要使用key來給每個節點做一個唯一標識,Diff算法就可以正確的識別此節點,找到正確的位置區插入新的節點。
所以一句話,key的作用主要是為了高效的更新虛擬DOM。另外vue中在使用相同標簽名元素的過渡切換時,也會使用到key屬性,其目的也是為了讓vue可以區分它們,否則vue只會替換其內部屬性而不會觸發過渡效果。