hello,我是小索奇哈,精心制作的Vue系列持续发放,涵盖大量的经验和示例,由浅入深进行讲解。 本章给大家讲解的是条件&列表渲染,前面的章节已经更新完毕,后面的章节持续输出,有任何问题都可以留言或私信哈,一起加油~
条件渲染
1. v-if
这个指令根据表达式的真假来决定是否渲染元素
例如:
<div v-if="show">
只有在 show = true 时显示
</div>
2. v-else-if/v-else
v-else-if表示v-if的“else-if”条件,v-else表示最后的“else”条件
例如:
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else>
都不是
</div>
3. 用key管理可复用元素
让Vue通过key值识别元素,提高渲染效率
例如:
<div v-if="loginType === 'username'" key="username">
username
</div>
<div v-else key="email">
email
</div>
4. v-show
基于CSS显示/隐藏元素,只是切换display
例如:
<div v-show="ok">
在 ok=true 时显示
</div>
如果要频繁的更改,使用v-show
会更好点,因为它仅是切换隐藏显示,效率更高
v-if 和 v-show 在条件渲染时的选择判断如下
-
当需要完全切换显示/隐藏元素时,如用户权限控制,使用 v-if它会直接操作 DOM,完全销毁或重新渲染元素
-
当只需要简单切换元素的显示/隐藏,如一些弹窗、提示,使用 v-show它只是切换 CSS 的 display属性,开销更小
-
当元素较简单,渲染开销小时,两者差异不大如果元素较复杂,v-show 会略快一些
-
如果需要频繁切换,使用 v-show;如果在运行时不大可能改变,使用 v-if
简单总结一下:
-
完全展示/隐藏用 v-if
-
简单切换显示/隐藏用 v-show
-
频繁切换用 v-show
-
运行时不变用 v-if
template上的v-if
template
元素是一个不可见的包装器,它可以容纳多个元素,但是它本身不会渲染到DOM中
常见的应用场景就是和v-if指令结合使用:
-
把v-if加在
<template>
上,可以有条件地渲染<template>
内部的多个元素 -
而不需要像v-if直接加在单个元素上那样,重复写很多重复的v-if条件判断
-
<template>
可以包含任意类型的元素,非常方便地组织元素块 -
最终渲染结果不包含
<template>
元素,只包含其内部的元素
用户信息展示案例
<template v-if="user.loggedIn">
<div>
<!-- 用户信息 -->
<p>用户名:{{user.name}}</p>
<p>积分:{{user.points}}</p>
</div>
<div>
<!-- 用户菜单 -->
<button>首页</button>
<button>设置</button>
</div>
</template>
<div v-else>
<!-- 登录提示 -->
<p>请登录后查看个人信息</p>
<button>去登录</button>
</div>
注意template只能和v-if使用,用v-show是不行的
列表渲染
v-for
用于基于数据多次渲染元素,通常用来渲染列表数据
<ul>
<li v-for="item in list">
{{ item }}
</li>
</ul>
模板 v-for
和v-if一样,可以在<template>
上使用v-for来渲染元素块
<template v-for="item in list">
<div>{{item.name}}</div>
<div>{{item.desc}}</div>
</template>
track-by
用来指定元素的唯一标识,提高渲染效率
<div v-for="item in list" :key="item.id"></div>
这里详解一下key
在Vue列表渲染时,就像一个todolist应用生成一长串的li元素当列表数据更新时,Vue需要知道具体哪个元素发生了变化,比如插入、删除、排序等
那么Vue如何识别每个元素的身份呢?这就是key的作用了
我们需要给每个元素添加一个独一无二的key值,来标识不同的元素:
<ul>
<li v-for="item in list" :key="item.id">
{{item.name}}
</li>
</ul>
这里我们使用每个数据item的id作为keykey的要求是唯一的,比如item的id就很适合作为key
有了key,Vue就可以准确地知道哪个元素发生了变化,比如某个元素被删除了,Vue会找到对应key的元素并销毁
这样就实现了高效的列表渲染
所以简单来说,key的作用是作为元素的唯一标识,用于提高Vue列表渲染时的性能,底层会复用DOM节点需要注意key必须唯一,不要使用索引index作为key
注意
一定要写key!虽然说不写key也不报错,但不写可能导致以下内容
-
列表重新渲染时,会直接创建新元素,而不是移动原有元素这会带来页面闪烁
-
使用Transition过渡动画时,会无法正确获取元素状态
-
丧失Vue内部对列表元素的身份识别能力,导致其他问题
这里仅列出以下几点,在react中不写key直接就会报错
当我们不写key的话,vue会自动把index索引作为key(下面介绍index)
-
直接通过索引修改数组,如 vm.items[0] = {},是非响应式的。应使用 Vue.set(vm.items, 0, {}) 或 vm.items.splice() 来修改数组。
官网的介绍
image-20230821000956765
索引
可以通过第二个参数获得索引值
<div v-for="(item, index) in list"></div>
上面的key也可以根据索引来定义
<div v-for="(item, index) in list"></div>
这里可能会有问题,能用index作为key吗?也可以,但是不推荐!
比如下面案例
<li v-for="(item, index) in list" :key="index">
{{ item }}
</li>
使用 index 作为 key 存在以下问题:
-
列表重新排序时,元素的 key 会变化,导致状态混乱(比如新添加一个索引为0索引的小索奇,它就替代了初始索引为0的张三,与原来进行对比,发现张三被小索奇怼跑了,这不闹的吗?)
-
不能准确判断变化的原因是索引变还是数据变
-
删除元素时,产生的新索引不会跟踪到原元素
正确写法,还是建议绑定到item.id
身上,使用每一项数据中稳定且唯一的标识作为 key!
注意以面试题形式存在
image-20230821002410628
拓展Diff算法
key是虚拟DOM对象的标识,当数据变化时,Vue就会根据新数据生成新的虚拟DOM
diff 算法是 Vue 和 React 等虚拟 DOM 框架实现高效 DOM 更新的关键算法它可以增量更新视图,避免重新渲染整个 DOM 树
diff算法的基本原理是:
-
将当前虚拟 DOM 和上一次虚拟 DOM 进行对比,找出变化的内容(没有变化的直接就复用了)
-
如果在旧虚拟节点中未找到与新虚拟节点相同的key,那么直接创建新的虚拟DOM,进行渲染,如果key同,那么就对比内容
-
不直接操作 DOM,而是将变化记录到一个JS 对象中
-
将这些变化一次性更新到实际的 DOM树上
简单示例
diff算法对比会发现,只需要将 B 换成 C 即可,无需重新渲染整个 UL这样避免了不必要的 DOM 操作
<!-- 上次虚拟DOM -->
<ul>
<li>A</li>
<li>B</li>
</ul>
<!-- 当前虚拟DOM -->
<ul>
<li>A</li>
<li>C</li>
</ul>
Vue 和 React 都使用类似的 diff 算法实现最小化更新实际 DOM 的目的,这带来非常高的性能优势
image-20230821005540298
数组更新检测
Vue包含一系列观察数组变异方法来响应式更新视图
像push、pop、splice等方法
列表过滤
用两组代码分别实现过滤效果
watch实现
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:'男'}
],
//存放过滤后的新数组
filPerons:[]
},
watch:{
keyWord:{
immediate:true,
handler(val){
this.filPerons = this.persons.filter((p)=>{
// 返回一个布尔值
return p.name.indexOf(val) !== -1
})
}
}
}
})
computed实现
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:'男'}
]
},
computed:{
filPerons(){
// 计算属性必备return
return this.persons.filter((p)=>{
// 返回一个布尔值,计算属性没有newValue属性,但可以通过用户输入的值来拿
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
初始时计算属性会默认调用get,当依赖的数据变化时,计算属性也会自动调用~从而实现过滤
当computed和watch都能实现时,优先考虑computed
拓展filter
filter方法的作用是:
-
它接收一个函数作为参数,这个函数会逐个处理数组中的每个元素
-
函数返回一个布尔值,true表示保留该元素,false表示过滤掉该元素
-
filter会返回一个新数组,包含执行函数返回true的元素
-
下面示例中的name代表callback函数的参数,表示当前正在遍历到的数组元素
const names = ['王美丽', '李小福', '张快乐', '赵细腻', '吉祥如意', '康健壮'];
const longNames = names.filter(name => name.length > 3);
const loudLongNames = longNames.map(name => name.toUpperCase());
console.log(loudLongNames);
// 输出:['李小福', '张快乐', '赵细腻', '吉祥如意']
这里我们过滤出了长度大于3个字的中文名字,然后把它们转换成大写,这里仅仅为了多用一个方法~
filter 最典型的用途就是过滤数组,接受判断条件并返回过滤后的新数组,
列表排序
案例:实现过滤+排序
<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 filPerons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
<input type="text">
</li>
</ul>
</div>
<script type="text/JS">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
keyWord:'',
sortType:0, //0原顺序 1降序 2升序
persons:[
{id:'001',name:'马冬梅',age:30,sex:'女'},
{id:'002',name:'周冬雨',age:31,sex:'女'},
{id:'003',name:'周杰伦',age:18,sex:'男'},
{id:'004',name:'温兆伦',age:19,sex:'男'}
]
},
computed:{
filPerons(){
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>
image.png
拓展
Arrays.sort()
const months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months);
// output: Array ["Dec", "Feb", "Jan", "March"]
const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);
// output: Array [1, 100000, 21, 30, 4]
如果提供了 比较函数compareFn
,所有非 undefined
的数组元素都会按照比较函数的返回值进行排序,所有的 undefined
元素都会被排序到数组的末尾,并且不调用 compareFn
image-20230822221418048
简单案例
let arr = [66,99,88]
arr.sort((a,b)=>{
// 前-后就是升序,相反则降序
return a-b
})
console.log(arr)
// [66, 88, 99]
如果对你有用,请点个免费的爱心叭~