列表渲染
1 基本列表
我们可以使用 v-for
指令基于一个数组来渲染一个列表。
与react类似,用了遍历生成同样结构的多个数据,必须要每个数据都起个名字/打个标识–key。
写法
v-for 指令的值需要使用 item in items
形式的特殊语法,其中 items
是源数据的数组,而 item
是迭代项的别名,也就是形参:
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
<li v-for="item in items">
{{ item.message }}
</li>
这里item
即为key,也就是形参。
1.1遍历数组
<body>
<div id="root">
<!--遍历数组-->
<h2>人员列表</h2>
<ul>
<li v-for="p in persons" :key="p.id">{{p.name}}-{{p.age}}</li>
</ul>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:19},
{id:'002',name:'李四',age:20},
{id:'003',name:'王五',age:21}
],
}
})
</script>
这里p
为形参。
v-for 也支持使用可选的第二个参数表示当前项的位置索引:
<li v-for="(p,index) in persons" :key="index">{{p.name}}-{{p.age}}</li>
in 可以换成 of 作为分隔符:
<li v-for="(p,index) of persons" :key="index">{{p.name}}-{{p.age}}</li>
1.2 遍历对象
可以使用 v-for 来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用 Object.keys() 的返回值来决定。
<h2>汽车信息</h2>
<ul>
<li v-for="(value,key) of car" :key="key">{{key}}:{{p.value}}</li>
</ul>
car:{
name:'奥运',
price:'50万',
color:'黑色'
},
三个参数(属性值,属性名,位置索引):
v-for="(value, key, index) in car
1.3 遍历字符串
<h2>汽车信息</h2>
<ul>
<li v-for="(char,index) of str" :key="index">{{char}}-{{index}}</li>
</ul>
str:'hello'
1.4 遍历指定次数
v-for 可以直接接受一个整数值。
<h2>汽车信息</h2>
<ul>
<li v-for="(number,index) of 5" :key="index">{{index}}-{{number}}</li>
</ul>
注意此处 n 的初值是从 1 开始而非 0。
2 key的作用与原理
为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,需要为每个元素对应的块提供一个唯一的 key。
下面通过一个案例来探索key是如何起作用的。在上面1.1的基础上,点击按钮persons新增一个,且自动在后面添加一个文本框。
<body>
<div id="root">
<!--遍历数组-->
<h2>人员列表</h2>
<button @click.once="add">添加一个老刘</button>
<ul>
<li v-for="(p,index) of persons" :key="p.id">
{{p.name}}-{{p.age}}
<input type="text">
</li>
<!--- 若key写"index"或者不写都会导致新增信息和文本框错位---->
</ul>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:19},
{id:'002',name:'李四',age:20},
{id:'003',name:'王五',age:21}
]
},
methods:{
add(){
const p = {id:'004',name:'老刘',age:23}
this.persons.unshift(p)
//在数组的前端添加项
}
}
})
</script>
模板中的所有元素的key在Vue征用后,生成真实DOM后不显示了
新虚拟DOM先与初始虚拟DOM进行对比,将不同的节点转为新真实DOM
若没有key,Vue自动将index作为key的值
小结
- 虚拟DOM中key的作用:key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下。
- 对比规则:
- 旧虚拟DOM中找到了与新虚拟DOM相同的key
(1)若内容没变,直接使用之前的真实DOM
(2)若内容变了,则生成新的真实DOM,随后替换调页面中之前的真实DOM - 旧虚拟DOM中未找到与新虚拟DOM相同的key
(1)创建新的真实DOM,随后渲染到页面
- 用index作为key可能会引发的问题:
(1)若对数据进行逆序添加、逆序 删除等破坏顺序的操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题,但效率低
(2)若结构中包含输入类DOM:会产生错误DOM更新 ==> 界面有问题 - 开发中如何选择key:
(1)最好使用每条数据的唯一标志作为key
(2)若不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的
3 列表过滤——模糊搜索
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyword">
<ul>
<li v-for="(p,index) of filpersons" :key="index">{{p.name}}-{{p.age}}-{{p.sex}}</li>
</ul>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
keyword:'',
persons:[
{id:'001',name:'马冬梅',age:19,sex:'女'},
{id:'002',name:'周冬雨',age:20,sex:'女'},
{id:'003',name:'周杰伦',age:21,sex:'男'},
{id:'004',name:'温兆伦',age:22,sex:'男'}
],
}
})
</script>
3.1 用侦听属性(监视属性)监视keyword是否改变
<li v-for="(p,index) of filpersons" :key="index">{{p.name}}-{{p.age}}-{{p.sex}}</li>
//数组改为filpersons
data:{
filpersons:[] //添加数组 filpersons
}
watch:{
keyword:{
immediate.true,//先调一次
handler(newvalue){
this.filpersons = this.persons.filter((p)=>{
return p.name.indexOf(newvalue) !== -1
//p为形参
//return 后面是匹配的条件
})
}
}
}
filter( ):返回值为数组,其为符合条件的所有元素,即数组元素是调用的数组的子集。若无符合条件的元素,则返回空数组
indexOf:用于查找子字符串再字符串中的位置:返回第一次出现的位置;若无则返回-1;若查找空字符串,则返回0。
3.2 用计算属性实现(更好
computed:{
filpersons(){
return this.persons.filter((p)=>{
return p.name.indexOf(this.keyword) !== -1
})
}
}
4 列表排序
有时,我们希望显示过过滤或排序后的内容,而不实际变更或重置原始数据。
案例 在上面3的基础上,新增按钮,点击可按照年龄进行排序,还可以恢复最开始的顺序。
新增3个按钮,并绑定对应的事件。
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyword">
<button @click='sortType=2'>年龄升序</button>
<button @click='sortType=1'>年龄降序</button>
<button @click='sortType=0'>原顺序</button>
<ul>
<li v-for="(p,index) of filpersons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
</body>
利用计算属性来返回排序后的内容。
<script type="text/javascript">
new Vue({
el:'#root',
data:{
keyword:'',
sortType:0, //0 原顺序,1 降序,2 升序
persons:[
{id:'001',name:'马冬梅',age:36,sex:'女'},
{id:'002',name:'周冬雨',age:25,sex:'女'},
{id:'003',name:'周杰伦',age:45,sex:'男'},
{id:'004',name:'温兆伦',age:18,sex:'男'}
],
},
computed:{
filpersons(){
const arr = this.persons.filter((p)=>{
return p.name.indexOf(this.keyword) !== -1
})
//判断是否需要排序
if(this.sortType){
arr.sort((p1,p2)=>{
return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age
//后-前:降 前-后:升
})
}
return arr
}
}
})
</script>
若点击年龄降序 按钮,sortType = 1
,计算属性重新计算,三元表达式返回 p2.age-p1.age
,sort函数按照降序排。升序同理。