使用v-for時必須新增唯一的key
在學習vue過程中遇到Elements in iteration expect to have ‘v-bind:key’ directives.’ 這個錯誤,查閱資料得知Vue 2.2.0+的版本里,當在元件中使用v-for時,key是必須設定的。
解決方式一:設定對應的key
注意上面key值不要用物件或是陣列作為key,用string或number作為key,否則報錯:[Vue warn] Avoid using non-primitive value as key, use string/number value instead.
v-for中的key
使用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>
上面這種是我們做專案中常用到的一種場景,因為不加key,vue現在直接報錯,所以我使用index作為key;下面列舉兩種常見的資料更新情況
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的那條資料,因此只要新渲染這一條資料就可以了,其他都是就複用之前的;
同理在react中使用map渲染列表時,也是必須加key,且推薦做法也是使用id,也是這個原因;
其實,真正的原因並不是vue和react怎麼怎麼,而是因為Virtual DOM 使用Diff演算法實現的原因,
下面大致從虛擬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只會替換其內部屬性而不會觸發過渡效果。