部分转自https://blog.csdn.net/cun_king/article/details/120714227

1 v-for指令

1.1 简介

  用于遍历。

  当在组件中使用 v-for 时,key 现在是必须的。它需要一个唯一id

  

1.2 遍历数组

<template>
    <p v-for="(item,index) in list" :key="index">索引{{index}}----{{item}}</p>
</template>
 
<script>
 export default {
    data () {
    return {
      list:[1,2,3],
    }
  },
 }
</script>

 

1.3 遍历对象数组

<template>
    <p v-for="(item,index) in list" :key="index">索引{{index}}--{{item.name}}--{{item.money}}</p>
</template>
 
<script>
 export default {
    data () {
    return {
      list:[
         { "name": "小米手机", "money": 100 }, 
         { "name": "华为手机", "money": 200 }, 
         { "name": "苹果手机", "money": 300 }, 
         { "name": "魅族手机", "money": 400 }
      ],
    }
  },
 }
</script>

 

1.4 遍历对象

<template>
     <!-- 注意:在遍历对象身上的键值对的时候, 除了 有  val  key  ,在第三个位置还有 一个 索引  -->
    <p v-for="(val, key, i) in list">值是: {{ val }} --- 键是: {{key}} -- 索引: {{i}}</p>
 
</template>
 
<script>
 export default {
    data () {
    return {
      list:{
        id: 1,
        name: '小明',
        gender: '男'
      },
    }
  },
 }
</script>

 

1.5 遍历字符串

<body>

    <div id="root">

     <ul>
        <li v-for="(value,index) of name">
            {{value}}:{{index}}
        </li>
     </ul>
   
    </div>
    
    <script type="text/javascript" >
    
     const vm = new Vue({
      el:'#root',
      data:{
       name:'杭州历史',
       
      }
    })
      
     </script>
    </body>

 

2 key属性

2.1 简介

  key在Vue是DOM对象的标识;
  进行v-for列表展示时,不指定key,默认key是index;
  如果数据只做展示使用,使用index作为key是没有任何问题的;
  如果使用index作为key,而后续操作会破坏顺序,一定会带来效率问题,严重时会渲染出错误的DOM

2.2 key的作用

  key就是一个标识,被使用在Vue中。再详细一点,key被使用在Vue中的虚拟DOM中,并不会出现在真实DOM中

 

2.3 仅展示列表时使用不同的key

2.3.1 示例1-不设置key

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>key的原理</title>

    <!--引入vue-->
    <script type="text/javascript" src="../js/vue.js"></script>
    
</head>
<div id="root">
    <h2>人员列表</h2>
    <ul>
        <li v-for="(p,index) in persons">
            {{p.name}}-{{p.age}}
        </li>
    </ul>
</div>
<body>
    <script type="text/javascript">
        const vm = new Vue({
            el:'#root',
            data:{
                persons:[
                    {'id':'001', 'name':'张三','age':'18'},
                    {'id':'002', 'name':'李四','age':'19'},
                    {'id':'003', 'name':'王五','age':'20'}
                ]
            }
        })
    </script>
</body>
</html>

  上述示例html文件中并没有使用到key,似乎也没有问题。当然,单纯地展示数据,不写key是不会存在问题的。

  

2.3.2 设置id为key

<li v-for="(p,index) in persons" :key="p.id">
    {{p.name}}-{{p.age}}
</li>

  页面展示结果和上图结果一模一样

  而如果我们在浏览器上查看元素,li上面也不会看到key属性的存在

  

 

2.3.3 设置index为key

<li v-for="(p,index) in persons" :key="index">
    {{p.name}}-{{p.age}}
</li>

  页面效果也是一样的

  截至目前,我们可以得到两个结论:

    

2.3.4 小结 

  1) 只做数据展示用,不写key是没有任何影响的;

  2)key不会出现在真实DOM中

  3)实际上,即使不写key,Vue在生成真实DOM时,也用到了key,默认是数据索引(index)

 

2.4 不仅仅是展示的时候

  在展示人员信息的基础上显示索引,并且添加一个按钮,功能是在头部添加人员信息

 

2.4.1 示例 index作为key

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>key的原理</title>

    <!--引入vue-->
    <script type="text/javascript" src="../js/vue.js"></script>
    <link rel="icon" href="../favicon.ico" type="image/x-icon" />

</head>
<div id="root">
    <h2>人员列表</h2>
    <button @click="add">添加一个老刘</button>
    <ul>
        <li v-for="(p,index) in persons" :key="index">
            {{p.name}}-{{p.age}}-{{index}}
        </li>
    </ul>
</div>
<body>
    <script type="text/javascript">
        const vm = new Vue({
            el:'#root',
            data:{
                persons:[
                    {'id':'001', 'name':'张三','age':'18'},
                    {'id':'002', 'name':'李四','age':'19'},
                    {'id':'003', 'name':'王五','age':'20'}
                ]
            },
            methods:{
                add(){
                    const p = {'id':'004', 'name':'老刘','age':'40'}
                    this.persons.unshift(p)
                }
            }
        })
    </script>
</body>
</html>

   点击按钮,添加一个新人物,这个时候索引发生了变化,新添加的人物“老刘”变为了索引0。

   也就是说,使用index作为key,那么元素的index是可能会变化的

 

2.4.2 示例 index作为key

  不展示索引了,改为输入框,在每个人物后面的输入框内写上人物的姓,观察新插入数据后原始数据的变化

<li v-for="(p,index) in persons" :key="index">
    {{p.name}}-{{p.age}}
    <input type="text">
</li>

    在输入框中输入内容,效果如上图所示,似乎没有什么不对,接下来就是见证奇迹的时刻

  添加老刘,出现了问题,和我们预想的不一样。如下图,输入框和姓名对不上了。

  

2.4.3 id作为key

如果修改为数据的唯一标识,则不会产生这样的问题
<li v-for="(p,index) in persons" :key="p.id">
    {{p.name}}-{{p.age}}
    <input type="text">
</li>

如下图,添加老刘后,名字和输入框还是对应的

 

3 key的原理

  那么为什么使用index作为key会出现上面的问题的,二使用id就不会呢?

 

3.1 虚拟dom

  要解释key的实现原理,就要引入Vue一个十分重要的概念——虚拟DOM

  给出一组数据和模板,Vue要把这些数据渲染到页面上,首先要生成虚拟DOM,然后根据虚拟DOM去生成真实的DOM,真实的dom就是页面上显示的。如果数据发生了改变,Vue会生成新的虚拟DOM,然后再去生成新的真实DOM,关键就是这个新的真实DOM的生成方式了。

  并不是直接通过新的虚拟DOM生成新的真实DOM,否则虚拟DOM一点用处也没有了。Vue的操作是,拿新的虚拟DOM与之前的旧的虚拟的DOM去做比较,这个比较通过key来对应的,会把key相同的新旧虚拟DOM进行比较。如果相同,直接延用之前虚拟dom生成的真实dom即可,如果不同,则生成新的真实DOM对象。

 

3.2 过程分析

3.2.1 key为index的情况

  根据数据生成真实DOM的流程如下:(注意,下图的真实DOM中输入框里的内容为生成页面后手动添加)

   然后,添加人物“老刘”,获取到一组新数据

   Vue拿新数据生成新的虚拟DOM,注意,由于老刘放到了数组最前面,所以数组里面的数据的index发生了变化

   在生成真实DOM时,就需要用新生成的虚拟DOM和原来的虚拟DOM作比较(一条一条分析)

 

   对比第一条,key为0,找到旧DOM中key为0的数据,发现“老刘-40”和“张三-18”不同,创建新的真实DOM;再往后,发现输入框一致(注意,比较的是虚拟DOM,输入框里面是没有输入的文字的,所以是一致的),不必重新渲染,直接使用原来真实DOM的内容。第一条内容就出现了,而这个输入框还携带有张三的姓

  对比第二条,key为1,找到旧DOM中key为1的数据,发现“张三-18”和“李四-19”不同,创建新的真实DOM;再往后,发现输入框一致,不必重新渲染,直接使用原来真实DOM的内容。第二条内容就出现了,而这个输入框还携带有李四的姓

  之后同理

  回顾这个过程,key是作为虚拟DOM中对象的唯一标识,标识出了数据的“身份信息”,Vue在虚拟DOM中会根据这个“身份标识”去对比内容,设计的初衷是为了节省资源开支,不必渲染重复的部分。在本示例中,不但带来了效率问题,还渲染出了错误的DOM,后果非常严重

 

3.2.2 key为id的情况

  直接进入添加“老刘”后的新旧DOM对比

  

  对比第一条,key为‘004’,发现在旧DOM中并不存在,直接生成新的真实的DOM。

  对比第二条,key为‘001’,发现旧DOM中key为‘001’的数据相同,直接将原来的真实的DOM拿过来使用。

  ……

  最后生成正确的DOM,节省了资源开支