相关文章推荐

基本列表

Vue 引入 ​ v-for ​ 来遍历列表,列表里的数据可以是 数组 对象 字符串 次数

v-for 指令

1)用于展示列表数据;

2)语法: ​ v-for="(item,index) in items" :key="key" ​​, ​ in ​​ 换用 ​ of ​ 也是可以的;

3)可遍历:数组、对象、字符串(用的少)、指定次数(用的少)


遍历数组

需求:遍历数组,将内容显示在列表中;

<div id="root">
<h2>{{title}}信息</h2>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>

提示1:引入 ​ v-for ​ 遍历数据,类似JS中 for...in 用法,(p,index) 理解为形参,persons 为遍历对象;

提示2:​ :key ​ 是Vue里的特殊属性,用于虚拟DOM的算法,目前理解需要值唯一就可以;

new Vue({
el:'#root',
data:{
title:'人员',
persons:[
{id:"001",name:"张三",age:18},
{id:"002",name:"李四",age:19},
{id:"003",name:"王五",age:20},
{id:"004",name:"赵六",age:21}
]
}
});

提示:persons 为数组,数组里又以对象的形式存储着人员信息,使用 id 一般是后台传来的数据,区别唯一性;

看效果:

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_列表渲染

遍历对象

需求:遍历对象,将内容显示在列表中;

<div id="root">
<h2>{{title}}信息</h2>
<ul>
<li v-for="(val,key) in car" :key="key">
{{key}}-{{val}}
</li>
</ul>
</div>

提示:遍历对象时的 key , val 顺序要注意一下;

new Vue({
el: '#root',
data: {
title: '汽车',
car: {
brand: 'BMW',
price: '28W',
color: 'blank',
weight: '5ton'
}
}
});

提示:以一个对象的形式存储的数据;

看下效果:

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_列表渲染_02

遍历字符串

需求:遍历字符串,将内容显示在列表中;

<div id="root">
<h2>{{title}}信息</h2>
<ul>
<li v-for="(char,index) in str" :key="index">
{{char}}-{{index}}
</li>
</ul>
</div>

提示:这次是字符串,注意 char 和 index , 其中 index 是指字符串的位置,从 0 开始读数;

new Vue({
el: '#root',
data: {
title: '字符串',
str:'hello'
}
});

遍历字符串,用的不多;

看下效果:

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_Key的原理_03

遍历次数

需求:遍历次数,将内容显示在列表中;

<div id="root">
<h2>{{title}}信息</h2>
<ul>
<li v-for="(number,index) in 5" :key="index">
{{number}}-{{index}}
</li>
</ul>
</div>

提示:这次只是遍历次数,直接 in 5,输出是数字和索引;

new Vue({
el: '#root',
data: {
title: '次数',
}
});

看下效果:

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_列表渲染_04


key的原理

:key ​ 就是给节点做身份标识,有唯一性;属性被 Vue 内部征用,也不会显示在DOM中;

v-for ​​ 遍历的时候,不写或忘写了 ​ :key ​​ ,Vue会拿索引 index 来补全 ​ :key="index" ​;

但有时,只拿索引当 key ,可能会出问题,下面会有错误演示;

需求:在列表中遍历数组,并添加一个新元素;

<div id="root">
<h2>{{title}}列表</h2>
<button @click.once="addPerson">添加老刘</button>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
</div>

提示:绑定 ​ @click.once ​ 事件,点击按钮,添加老刘信息,once 是只点一次的修饰符;

new Vue({
el: '#root',
data: {
title: '人员',
persons: [
{ id: "001", name: "张三", age: 18 },
{ id: "002", name: "李四", age: 19 },
{ id: "003", name: "王五", age: 20 },
]
},
methods: {
addPerson(){
this.persons.push({id:'004',name:'老刘',age:40});
}
},
});

提示:添加一个方法,用来操作数组,使用 ​ push() ​ 方法压入 “老刘” 对象的数据;

看下DOM:

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_列表渲染_05

注意:DOM元素中,没有 key ,不显示,是Vue内部使用的;

看下效果:没有问题;

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_Vue_06


错误演示

为了演示使用索引当 key 会有错误,代码要改一下;

<div id="root">
<h2>{{title}}列表</h2>
<button @click.once="addPerson">添加老刘</button>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
</div>

提示:在每行输出后面,加个 input ;

注意:这里使用了  ​ :key="index" ​,数组的索引,问题会出在这个上面;

new Vue({
el: '#root',
data: {
title: '人员',
persons: [
{ id: "001", name: "张三", age: 18 },
{ id: "002", name: "李四", age: 19 },
{ id: "003", name: "王五", age: 20 },
]
},
methods: {
addPerson(){
// this.persons.push({id:'004',name:'老刘',age:40});
this.persons.unshift({id:'004',name:'老刘',age:40});
}
},
});

提示:这次不是把 "老刘" ​ push() ​​ 到数组后面,而是添加到数组的最前面 ​ unshift() ​;

看下效果:

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_Key的原理_07

提示:在每行数据后面,添加个 input ,里面再添加上对应的人员信息,此时再点按钮,就出问题了;

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_列表渲染_08

为什么会都往上窜了一位?


原理解析

用 index 作为 key 时:

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_列表渲染_09

新旧的虚拟DOM会做比较,在每一行中先找Key,找到相同 key 时,把没变的内容使用之前的真实DOM,把变了的内容生成新的真实DOM;

本例中:

第一行对比时,找到了相同的 【Key=“0”】,会把相同的部分 【<input type="text">】 使用之前真实的DOM,把不同的部分 【老刘-30】生成新的真实DOM。

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_Key的原理_10

问题就出在:之前真实的DOM【<input type="text">】是有内容的,但虚拟DOM里没有体现出来,所以Vue认为新旧虚拟DOM的 【<input type="text">】是一样的,就拿来复用了。挨个往下比较,就都有这个问题,每行都在生成新的DOM,每行又都复用了原本有数据的 <input> ,导致渲染效率变低,界面上的数据还出现了错位。最终结果就是在上例看到的效果:

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_Vue_11


解决这个问题的办法就是:改用 id 作为 Key。

​如果本例改用 ​​ :key="p.id" 来替换索引 index :就不会出现在数组最前面添加数据导致索引变化了;

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_Vue_12

那么,在虚拟DOM比较时,新添加的数据在旧的虚拟DOM中,是找不到相同 key 的,那么这一行新的数据会完整的生成新的真实DOM,剩下的每一行都能和旧的虚拟DOM一一对应,就不需要再生成新的DOM了,复用即可,这样就不存在效率的浪费;

Vue2(笔记10) - Vue核心 - 列表渲染、Key的原理_列表渲染_13

最后:知道了 key 的原理 索引index 的变化 ,之后在遍历时就知道更准确的使用 index 或干脆使用 id 一类的唯一标识;


面试题

react、vue 中的 key  有什么作用? (key 的内部原理)

1)虚拟DOM中的Key的作用:

Key 是虚拟DOM对象的标识,当数据发生变化时,Vue会根据“新数据”生成“新的虚拟DOM”,随后Vue进行“新的虚拟DOM”和“旧的虚拟DOM”的差异比较(diff算法);

2)对比规则:

1、旧虚拟DOM中找到了与新虚拟DOM相同的Key:

1)若虚拟DOM中内容没变,直接使用之前的真实DOM!

2)若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM;

2、旧虚拟DOM中未找到与新虚拟DOM相同的Key:

创建新的真实DOM,随后渲染到页面;

3)用 Index 作为 Key 可能会引发的问题:

1、若对数据进行:逆序添加、逆序删除等破坏顺序的操作:会产生没有必要的真实DOM更新 ==》 界面效果没问题,但效率低。

2、如果结构中还包含输入类的DOM(如input):会产生错误DOM更新 ==》界面有问题;

4)开发中如何选择Key?

1、最好使用每条数据的唯一标识作为Key,比如:id、手机号、身份证号、学号等唯一值;

2、如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表用于展示,使用 index 作为Key是没有问题的。


 
推荐文章