基本列表
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 一般是后台传来的数据,区别唯一性;
看效果:
遍历对象
需求:遍历对象,将内容显示在列表中;
<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'
}
}
});
提示:以一个对象的形式存储的数据;
看下效果:
遍历字符串
需求:遍历字符串,将内容显示在列表中;
<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'
}
});
遍历字符串,用的不多;
看下效果:
遍历次数
需求:遍历次数,将内容显示在列表中;
<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: '次数',
}
});
看下效果:
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:
注意:DOM元素中,没有 key ,不显示,是Vue内部使用的;
看下效果:没有问题;
错误演示
为了演示使用索引当 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()
;
看下效果:
提示:在每行数据后面,添加个 input ,里面再添加上对应的人员信息,此时再点按钮,就出问题了;
为什么会都往上窜了一位?
原理解析
用 index 作为 key 时:
新旧的虚拟DOM会做比较,在每一行中先找Key,找到相同 key 时,把没变的内容使用之前的真实DOM,把变了的内容生成新的真实DOM;
本例中:
第一行对比时,找到了相同的 【Key=“0”】,会把相同的部分 【<input type="text">】 使用之前真实的DOM,把不同的部分 【老刘-30】生成新的真实DOM。
问题就出在:之前真实的DOM【<input type="text">】是有内容的,但虚拟DOM里没有体现出来,所以Vue认为新旧虚拟DOM的 【<input type="text">】是一样的,就拿来复用了。挨个往下比较,就都有这个问题,每行都在生成新的DOM,每行又都复用了原本有数据的 <input> ,导致渲染效率变低,界面上的数据还出现了错位。最终结果就是在上例看到的效果:
解决这个问题的办法就是:改用 id 作为 Key。
如果本例改用
:key="p.id"
来替换索引 index :就不会出现在数组最前面添加数据导致索引变化了;
那么,在虚拟DOM比较时,新添加的数据在旧的虚拟DOM中,是找不到相同 key 的,那么这一行新的数据会完整的生成新的真实DOM,剩下的每一行都能和旧的虚拟DOM一一对应,就不需要再生成新的DOM了,复用即可,这样就不存在效率的浪费;
最后:知道了
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是没有问题的。