很多人一說起這道老生常談的面試題,馬上就開始滔滔不絕地講述 虛擬DOM
和 diff演演算法
了。
講這些沒問題,但如果是我,一定先講 v-for 的 key 值寫成 index 會造成的問題,再講原理。
曾經我寫 v-for, key 值永遠都是 index,直到有一天,我這麼寫造成了線上bug...
來看一下我的線上bug演示吧:
父元件程式碼 <Child v-for="(item, index) in list" :key="index" :count="item.count" :name="item.name" @delete="handleDelete(index)" /> list: [ { count: 1, name: '第1個元素' }, { count: 2, name: '第2個元素' }, { count: 3, name: '第3個元素' } ] handleDelete(index) { this.list.splice(index, 1) },
如程式碼和gif演示,點選刪除第2個元素,看上去似乎一切正常。
等一下,第三個元素的count值居然變成了2,wtf!!!
驚得我又去看了遍子元件的程式碼
子元件 <div> <span>{{ name }}</span> count值為:{{ innerCount }} <button @click="$emit('delete')">-</button> </div> props: { count: { type: Number, default: 0 }, name: { type: String, default: '' } }, data() { return { innerCount: this.count } }
感覺也沒什麼不對的啊。
不信邪,我又多建立了點元素來刪除,還試了下排序:
果然,不光刪除元素有問題,排序也有問題。
把 key 值改成 item.name 再試一下。
<Child v-for="(item, index) in list" :key="item.name" :count="item.count" :name="item.name" @delete="handleDelete(index)" />
正常了。
這樣看來,在 v-for 裡把 key 值寫成 index,非常危險啊。
在查閱了 vue 官方檔案之後,我終於明白了原因:
當 Vue 正在更新使用
v-for
渲染的元素列表時,它預設使用「就地更新」的策略。如果資料項的順序被改變,Vue 將不會移動 DOM 元素來匹配資料項的順序,而是就地更新每個元素,並且確保它們在每個索引位置正確渲染。
這個預設的模式是高效的,但是只適用於不依賴子元件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出。
不依賴子元件狀態
子元件裡有一行很關鍵的程式碼
data() { return { innerCount: this.count } }
子元件內部定義了 innerCount,這樣子元件就有了自己的狀態,按照官方檔案的說明,這種情況下不能把 index 作為 key 值。
臨時 DOM 狀態
<div v-for="(item, index) in list1" :key="index"> <input type="text" /> <button @click="delClick(index)">刪除</button> </div>
刪除了第2項,但是第3項在表單中的3變成了2,跟上面依賴子元件狀態的例子是一樣的。
寫列表渲染時, 依賴子元件狀態或臨時 DOM 狀態的情況,如果有 刪除、增加、排序這樣的功能,不要把 index 作為 key。
事實上,寫列表渲染時,永遠不要把 index 做為 key,key 一定要是唯一標識。
至於原因,就要理解 diff
演演算法之後才能明白了。
待解答問題:
彆著急,立了個寫100個 vue 問題相關文章的 flag 呢,後面的文章再慢慢分析。
希望我的 vue 系列文章能對前端路上的你有幫助~
【相關推薦:】
以上就是vue中為什麼v-for指令的 key 值不能是 index?的詳細內容,更多請關注TW511.COM其它相關文章!