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
相关文章
相关标签/搜索