key的作用是什么?
簡單來說:
key是給每一個vnode的唯一id,可以
依靠key
,更准確
, 更快
的拿到oldVnode中對應的vnode節點。
1. 更准確
因為帶key就不是就地復用
了,在sameNode函數 a.key === b.key
對比中可以避免就地復用的情況。所以會更加准確。
2. 更快
利用key的唯一性生成map對象來獲取對應節點,比遍歷方式更快。(這個觀點,就是我最初的那個觀點。從這個角度看,map會比遍歷更快。)
詳解:
部分討論是基於沒有key的情況diff速度會更快。確實,這種觀點並沒有錯。沒有綁定key的情況下,並且在遍歷模板簡單
的情況下,會導致虛擬新舊節點對比更快,節點也會復用。而這種復用是就地復用
,一種鴨子辯型
的復用。以下為簡單的例子:
<div id="app"> <div v-for="i in dataList">{{ i }}</div> </div>
var vm = new Vue({ el: '#app', data: { dataList: [1, 2, 3, 4, 5] } })
以上的例子,v-for的內容會生成以下的dom節點數組,我們給每一個節點標記一個身份id:
[
'<div>1</div>', // id: A '<div>2</div>', // id: B '<div>3</div>', // id: C '<div>4</div>', // id: D '<div>5</div>' // id: E ]
- 改變dataList數據,進行數據位置替換,對比改變后的數據
vm.dataList = [4, 1, 3, 5, 2] // 數據位置替換 // 沒有key的情況, 節點位置不變,但是節點innerText內容更新了 [ '<div>4</div>', // id: A '<div>1</div>', // id: B '<div>3</div>', // id: C '<div>5</div>', // id: D '<div>2</div>' // id: E ] // 有key的情況,dom節點位置進行了交換,但是內容沒有更新 // <div v-for="i in dataList" :key='i'>{{ i }}</div> [ '<div>4</div>', // id: D '<div>1</div>', // id: A '<div>3</div>', // id: C '<div>5</div>', // id: E '<div>2</div>' // id: B ]
增刪dataList列表項
vm.dataList = [3, 4, 5, 6, 7] // 數據進行增刪 // 1. 沒有key的情況, 節點位置不變,內容也更新了 [ '<div>3</div>', // id: A '<div>4</div>', // id: B '<div>5</div>', // id: C '<div>6</div>', // id: D '<div>7</div>' // id: E ] // 2. 有key的情況, 節點刪除了 A, B 節點,新增了 F, G 節點 // <div v-for="i in dataList" :key='i'>{{ i }}</div> [ '<div>3</div>', // id: C '<div>4</div>', // id: D '<div>5</div>', // id: E '<div>6</div>', // id: F '<div>7</div>' // id: G ]
從以上來看,不帶有key,並且使用簡單的模板,基於這個前提下,可以更有效的復用節點,diff速度來看也是不帶key更加快速的,因為帶key在增刪節點上有耗時。這就是vue文檔所說的默認模式
。但是這個並不是key作用,而是沒有key的情況下可以對節點就地復用
,提高性能。
這種模式會帶來一些隱藏的副作用,比如可能不會產生過渡效果,或者在某些節點有綁定數據(表單)狀態,會出現狀態錯位。VUE文檔也說明了 這個默認的模式是高效的,但是只適用於不依賴子組件狀態或臨時 DOM 狀態 (例如:表單輸入值) 的列表渲染輸出
在不帶key的情況下,對於簡單列表頁渲染
來說diff節點更快是沒有錯誤的。但是這並不是key的作用呀。