key
的特殊attribute
主要用在Vue
的虛擬DOM
算法,在新舊Nodes
對比時辨識VNodes
。若是不使用key
,Vue
會使用一種最大限度減小動態元素而且儘量的嘗試就地修改、複用相同類型元素的算法,而使用key
時,它會基於key
的變化從新排列元素順序,而且會移除key
不存在的元素。此外有相同父元素的子元素必須有獨特的key
,重複的key
會形成渲染錯誤。javascript
首先是官方文檔的描述,當Vue
正在更新使用v-for
渲染的元素列表時,它默認使用就地更新的策略,若是數據項的順序被改變,Vue
將不會移動DOM
元素來匹配數據項的順序,而是就地更新每一個元素,而且確保它們在每一個索引位置正確渲染。這個默認的模式是高效的,可是隻適用於不依賴子組件狀態或臨時DOM
狀態的列表渲染輸出,例如表單輸入值。爲了給Vue
一個提示,以便它能跟蹤每一個節點的身份,從而重用和從新排序現有元素,你須要爲每項提供一個惟一 key attribute
,建議儘量在使用v-for
時提供key attribute
,除非遍歷輸出的DOM
內容很是簡單,或者是刻意依賴默認行爲以獲取性能上的提高。
簡單來講,當在列表循環中使用key
時,須要使用key
來給每一個節點作一個惟一標識,diff
算法就能夠正確的識別此節點,找到正確的位置直接操做節點,儘量地進行重用元素,key
的做用主要是爲了高效的更新虛擬DOM
。此外,使用index
做爲key
是並不推薦的作法,其只能保證Vue
在數據變化時強制更新組件,以免原地複用帶來的反作用,但不能保證最大限度的元素重用,且使用index
做爲key
在數據更新方面和不使用key
的效果基本相同。html
首先定義一個Vue
實例,渲染四個列表,分別爲簡單列表與複雜列表,以及其分別攜帶key
與不攜帶key
時對比其更新渲染時的速度,本次測試使用的是Chrome 81.0
,每次在Console
執行代碼時首先會進行刷新從新加載界面,避免瀏覽器以及Vue
自身優化帶來的影響。vue
<!DOCTYPE html> <html> <head> <title>Vue</title> </head> <body> <div id="app"> <ul> <li v-for="item in simpleListWithoutKey" >{{item}}</li> </ul> <ul> <li v-for="item in simpleListWithKey" :key="item" >{{item}}</li> </ul> <ul> <li v-for="item in complexListWithoutKey"> <span v-for="value in item.list" v-if="value > 5">{{value}}</span> </li> </ul> <ul> <li v-for="item in complexListWithKey" :key="item.id"> <span v-for="value in item.list" :key="value" v-if="value > 5">{{value}}</span> </li> </ul> </div> </body> <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script> <script type="text/javascript"> var vm = new Vue({ el: '#app', data: { simpleListWithoutKey: [1, 2, 3, 4, 5, 6], simpleListWithKey: [1, 2, 3, 4, 5, 6], complexListWithoutKey:[ {id: 1, list: [1, 2, 3]}, {id: 2, list: [4, 5, 6]}, {id: 3, list: [7, 8, 9]} ], complexListWithKey:[ {id: 1, list: [1, 2, 3]}, {id: 2, list: [4, 5, 6]}, {id: 3, list: [7, 8, 9]} ], } }) </script> </html>
在簡單列表的狀況下,不使用key
可能會比使用key
的狀況下在更新時的渲染速度更快,這也就是官方文檔中提到的,除非遍歷輸出的DOM
內容很是簡單,或者是刻意依賴默認行爲以獲取性能上的提高。在下面的例子中能夠看到沒有key
的狀況下列表更新時渲染速度會快,當不存在key
的狀況下,這個列表直接進行原地複用,原有的節點的位置不變,原地複用元素,將內容更新爲5
、6
、7
、8
、9
、10
,並添加了11
與12
兩個節點,而存在key
的狀況下,原有的1
、2
、3
、4
節點被刪除,5
、6
節點保留,添加了7
、8
、9
、10
、11
、12
六個節點,因爲在DOM
的增刪操做上比較耗時,因此表現爲不帶key
的狀況下速度更快一些。java
// 沒有key的狀況下 console.time(); vm.simpleListWithoutKey = [5, 6, 7, 8, 9, 10, 11, 12]; vm.$nextTick(() => console.timeEnd()); // default: 2.193056640625ms
// 存在key的狀況下 console.time(); vm.simpleListWithKey = [5, 6, 7, 8, 9, 10, 11, 12]; vm.$nextTick(() => console.timeEnd()); // default: 3.2138671875ms
原地複用可能會帶來一些反作用,文檔中提到原地複用這個默認的模式是高效的,可是隻適用於不依賴子組件狀態或臨時DOM
狀態的列表渲染輸出,例如表單輸入值。在不設置key
的狀況下,元素中沒有與數據data
綁定的部分,Vue
會默認使用已經渲染的DOM
,而綁定了數據data
的部分會進行跟隨數據渲染,假如操做了元素位置,則元素中未綁定data
的部分會停留在原地,而綁定了data
的部分會跟隨操做進行移動,在下面的例子中首先須要將兩個A
以後的輸入框添加數據信息,這樣就製做了一個臨時狀態,若是此時點擊下移按鈕,那麼不使用key
的組中的輸入框將不會跟隨下移,且B
到了頂端併成爲了紅色,而使用key
的組中會將輸入框進行下移,且A
依舊是紅色跟隨下移。git
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>就地複用</title> </head> <body> <div id="app"> <h3>採用就地複用策略(vuejs默認狀況)</h3> <div v-for='(p, i) in persons'> <span>{{p.name}}<span> <input type="text"/> <button @click='down(i)' v-if='i != persons.length - 1'>下移</button> </div> <h3>不採用就地複用策略(設置key)</h3> <div v-for='(p, i) in persons' :key='p.id'> <span>{{p.name}}<span> <input type="text"/> <button @click='down(i)' v-if='i != persons.length - 1'>下移</button> </div> </div> <script src="https://cdn.staticfile.org/vue/2.2.2/vue.min.js"></script> <script> new Vue({ el: '#app', data: { persons: [ { id: 1, name: 'A' }, { id: 2, name: 'B' }, { id: 3, name: 'C' } ] }, mounted: function(){ // 此DOM操做將兩個A的顏色設置爲紅色 主要是爲了演示原地複用 document.querySelectorAll("h3 + div > span:first-child").forEach( v => v.style.color="red"); }, methods: { down: function(i) { if (i == this.persons.length - 1) return; var listClone = this.persons.slice(); var one = listClone[i]; listClone[i] = listClone[i + 1]; listClone[i + 1] = one; this.persons = listClone; } } }); </script> </body> </html> <!-- 源於 https://www.zhihu.com/question/61078310 @霸都丶傲天 有修改-->
使用key
不只可以避免上述的原地複用的反作用,且在一些操做上可能可以提升渲染的效率,主要體如今從新排序的狀況,包括在中間插入和刪除節點的操做,在下面的例子中沒有key
的狀況下從新排序會原地複用元素,可是因爲v-if
綁定了data
因此會一併進行操做,在這個DOM
操做上比較消耗時間,而使用key
得狀況則直接複用元素,v-if
控制的元素在初次渲染就已經決定,在本例中沒有對其進行更新,因此不涉及v-if
的DOM
操做,因此在效率上會高一些。github
console.time(); vm.complexListWithoutKey = [ {id: 3, list: [7, 8, 9]}, {id: 2, list: [4, 5, 6]}, {id: 1, list: [1, 2, 3]}, ]; vm.$nextTick(() => console.timeEnd()); vm.$nextTick(() => console.timeEnd()); // default: 4.100244140625ms
console.time(); vm.complexListWithKey = [ {id: 3, list: [7, 8, 9]}, {id: 2, list: [4, 5, 6]}, {id: 1, list: [1, 2, 3]}, ]; vm.$nextTick(() => console.timeEnd()); // default: 3.016064453125ms
https://github.com/WindrunnerMax/EveryDay
https://cn.vuejs.org/v2/api/#key https://www.jianshu.com/p/4bdd2690859c https://www.zhihu.com/question/61078310 https://segmentfault.com/a/1190000012861862 https://www.cnblogs.com/zhumingzhenhao/p/7688336.html https://blog.csdn.net/hl18730262380/article/details/89306500 https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/1 https://cn.vuejs.org/v2/guide/list.html#%E7%BB%B4%E6%8A%A4%E7%8A%B6%E6%80%81