文檔:https://cn.vuejs.org/v2/guide/list.html
當 Vue.js 用
v-for
正在更新已渲染過的元素列表時,它默認用“就地復用”策略。如果數據項的順序被改變,Vue 將不會移動 DOM 元素來匹配數據項的順序, 而是簡單復用此處每個元素,並且確保它在特定索引下顯示已被渲染過的每個元素。這個類似 Vue 1.x 的track-by="$index"
意思就是,默認就是按照索引來“就地復用”html元素的,如以下代碼
<div v-for="(item,index) in arr" :key="index">
等價於
<div v-for="(item,index) in arr">
對於“就地復用”這個現象,以下來重現一下:
測試代碼
<template> <div> <div v-for="(item,index) in arr"> <input type="text"> <button @click="del(index)">刪除</button> </div> <button @click="add">添加</button> </div> </template> <script> export default { name: "App", data() { return { arr: [ "1", "2", "3", ] } }, methods: { del(index) { this.arr.splice(index, 1); }, add() { this.arr.push(""); } } } </script>
往頁面的輸入框依次填入1~3:
然后點擊第二個刪除按鈕,效果如下:
頁面剩下1、2,這跟我們預期的剩下1、3不一樣,原因就在於vue默認的“就地復用”原則。現象解釋如下:
將以上三個輸入框記為a,b,c。for循環默認的key為索引的話,則a對應0,b對應1,c對應2 。那當刪了了第二個元素時,新數組的元素的索引分別為0和1,而重新渲染時,采用就地復用的話,復用到的dom元素就是a和b了,頁面輸入框就展示1和2了。這輸入框中的1和2實際上就是代表了dom的狀態,通過輸入框的值,就能看出來,vue復用了哪個dom元素。這里說的,實際上就是對應了文檔的第二段話:
這個默認的模式是高效的,但是只適用於不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出
也就是說,當dom有狀態的時候,最好就不要采用這種默認模式(key為索引),否則會導致狀態混亂(如上面我們明明點擊了第二個刪除,而頁面展示的效果卻像點擊了第三個刪除的按鈕一樣)。
對於循環渲染有狀態的dom元素,應該讓key與數組元素一一對應起來,這樣數組元素的刪除,就完全等同於對應dom元素的刪除了(換個角度解釋以上的問題就是:點擊刪除的前后,索引1對應着相同的dom元素,而對應的數組元素卻不一致,導致頁面展示的結果讓人困惑),解決辦法如下:
<div v-for="(item,index) in arr" :key="item"> <input type="text"> <button @click="del(index)">刪除</button> </div>
讓key與數組元素唯一對應起來即可,運行效果:點擊第二個刪除,界面上剩余1,3,符合我們預期結果。但是這樣一來,vue就不會就地復用,性能會相對低一點了。