Vue-for的key值到怎么設置?
在Vue項目中,v-for所在的DOM上,如果不設置key值,編輯器會警告;如果開啟了eslint,eslint檢查會提示需要給v-for設置key值 。我在做代碼CR時,發現很多同學喜歡設置key值為唯一標識,像以下代碼第二行設置為item.id。可能你也試過設置:key="index"也沒有問題。那么Vue的v-for key值到底怎么設置?
<div v-for="item in list"></div> <div v-for="item in list" :key="item.id"></div> <div v-for="(item,index) in list" :key="index"></div> 復制代碼
當我不設置key時
上代碼!
<div id="app"> <ul> <li v-for="item in list"> <span>{{item.name}}</span> <input type="text" /> </li> </ul> <button @click="exchange">交換</button> </div> <script> const app = new Vue({ el: "#app", data: { list: [ { id: 111, name: "楊倩", }, { id: 222, name: "楊皓然", }, ], }, methods: { exchange() { this.list.reverse(); }, }, }); </script> 復制代碼
list數組有兩對象,渲染成兩行,每一行包括name和一個輸入框;另外有一個“交換”按鈕,點擊會交換數組兩個對象。
以下圖片分別為“交換”前后。可以看到當數組元素交換時,界面元素也交換了。


有沒有DOM交換
我們使用Vue盡量不要操作DOM,因為Vue代替我們操作DOM。那么當數組元素交換時,Vue是如何操作DOM,進而交換界面元素呢?兩種方式:
- DOM交換
- DOM不交換,更新DOM的值

所以究竟有沒有DOM交換? 如果DOM不交換,更新DOM的值的話,只需要更新兩個span標簽的值,而input是無須交換的。這樣我們在輸入框中分別輸入1,2,再點擊“交換”按鈕,觀察到:


input標簽的值並沒有變,也就是並沒有交換liDOM !
想進一步證明,你也可以用js獲取li所在的dom元素,交換后,再重新獲取,用
===對比。測試結果一樣,並沒有交換DOM
v-for key設置為index
我們再修改代碼,把v-for key值設置為index,觀察下是否會交換DOM
<li v-for="(item,index) in list" :key="index"> <span>{{item.name}}</span> <input type="text" /> </li> 復制代碼
觀察到交換前后和上面情況一致,input輸入框的值也沒有交換,也就是說並沒有交換DOM
v-for key設置為唯一標識
我們再修改代碼,把v-for key值設置為item.id,觀察下是否會交換DOM
<li v-for="item in list" :key="item.id"> <span>{{item.name}}</span> <input type="text" /> </li> 復制代碼

交換前后input輸入框的值也交換了,也就是說數組元素交換,對應渲染的DOM也交換了!
分析原因
v-for的key值三種設置方式,只有設置為唯一標識時,DOM才交換,為什么呢?很簡單,因為DOM交換耗性能,而Vue懶! 如果Demo沒有input框,交換數組元素時,需要交換name,那么交換DOM和不交換DOM方式都是正確的。 當 Vue 正在更新使用 v-for 渲染的元素列表時,它默認使用“就地更新”的策略,Vue官方文檔鏈接
解釋以上三種key值設置
- 不設置key值。默認使用“就地更新”,數組元素更新,更新對應的DOM的值。
2.設置key值為index。設置key值,Vue能區分已渲染的DOM,但是數組交換后改變了兩個元素各自的下標。所以原下標為1的元素,變成下標為0的元素,渲染原下標為0的DOM,無須交換DOM。 3.設置Key為item.id。item.id是唯一標識,Vue能區分已經渲染的DOM,元素交換后,DOM也會交換。
vfor的key設置為唯一標識效率高?!!
如果是的話,vfor內部實現時,怎么不直接設置key=Date.now(),即默認唯一標識。如果Vue源碼沒有實現,我們在寫一個指令替換vfor即可。所以之所以Vue源碼默認不設置vfor的key值為唯一標識,就是因為大多數情況下,就地更新效率更高!!! 我們使用v-for的大多數場景是這樣: 聲明data屬性為空數組,請求賦值,在結構中使用vfor渲染;再多一點邏輯就是更新數組元素的某一項和在給數組末尾新增一項。 想象一下以上場景中,Vue是如何進行DOM操作的。賦值就新增DOM元素,數組增加元素也是新增DOM元素,如果某元素更新呢?數組元素下標和已經渲染的DOM下標一致,找到然后更新值即可!
<div id="app"> <!-- 在結構中使用vfor渲染list --> <div v-for="item in items" :key="item.id"> {{ item }} </div> </div> <script> const app = new Vue({ el: "#app", data: { list: [], }, created(){ // 請求獲取數據,賦值給list this.list = this.$request(...) } }); </script> 復制代碼
當然如果是給數組中間插入一個元素,key值為唯一標識的確效率高些。這個和虛擬DOM有關,中文技術的世界這塊資料已經泛濫泛濫,大家自行查找。這可能也是常見代碼里面key值設置為唯一標識的原因。
結論
關於v-for的key值設置
- 如果已渲染的列表元素下標不會有變化,key值設置為index即可(防止eslint警告)
- 如果已渲染的列表元素下標會有變化,key值請設置為唯一標識
-
- diff算法默認使用“就地復用”的策略,是一個首尾交叉對比的過程。
- 用index作為key和不加key是一樣的,都采用“就地復用”的策略
- “就地復用”的策略,只適用於不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出。
- 將與元素唯一對應的值作為key,可以最大化利用dom節點,提升性能
