Vue中key的作用
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
的效果基本上相同。
範例
先定義一個Vue
實例,渲染四個列表,分別為簡單列表與複雜列表,以及其分別攜帶key
與不攜帶key
時對比其更新渲染時的速度,本測試使用的是Chrome 81.0
,每次在Console
執行程式碼時首先會進行刷新重新載入介面,避免瀏覽器以及Vue
自身優化帶來的影響。
<!DOCTYPE html> <html> <head> <title>Vue</title> </head> <body> <p 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> </p> </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
的情況下速度更快一些。
// 没有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
依旧是红色跟随下移。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>就地复用</title> </head> <body> <p id="app"> <h3>采用就地复用策略(vuejs默认情况)</h3> <p v-for='(p, i) in persons'> <span>{{p.name}}<span> <input type="text"/> <button @click='down(i)' v-if='i != persons.length - 1'>下移</button> </p> <h3>不采用就地复用策略(设置key)</h3> <p 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> </p> </p> <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 + p > 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
操作,所以在效率上会高一些。
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
以上是程式碼詳解Vue中key的作用範例的詳細內容。更多資訊請關注PHP中文網其他相關文章!