Vue中key的做用

Vue中key的做用

key的特殊attribute主要用在Vue的虛擬DOM算法,在新舊Nodes對比時辨識VNodes。若是不使用keyVue會使用一種最大限度減小動態元素而且儘量的嘗試就地修改、複用相同類型元素的算法,而使用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的狀況下,這個列表直接進行原地複用,原有的節點的位置不變,原地複用元素,將內容更新爲5678910,並添加了1112兩個節點,而存在key的狀況下,原有的1234節點被刪除,56節點保留,添加了789101112六個節點,因爲在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-ifDOM操做,因此在效率上會高一些。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
相關文章
相關標籤/搜索