Vue2教程详解二(class与style绑定、条件渲染、列表渲染、列表过滤、列表排序)
class与style绑定操作元素的class列表和内联样式是数据绑定的一个常见需求。因为它们都是attribute,所以我们可以用v-bind处理它们,只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将v-bind用于class和style时,Vue做了专门的增强,表达式结果的类型除了字符串以外,还可以是数组或对象;绑定HTML Class表达式是字符串<styl
目录
class与style绑定
操作元素的class列表和内联样式是数据绑定的一个常见需求。因为它们都是attribute,所以我们可以用v-bind处理它们,只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将v-bind用于class和style时,Vue做了专门的增强,表达式结果的类型除了字符串以外,还可以是数组或对象;
绑定HTML Class
- 表达式是字符串
字符串写法适用于:类名不确定,要动态获取场景
<style>
.sad {
font-size: large;
color: blue
}
</style>
<div id="root">
<h2 :class="moodlabel" @click="changeMood">今天心情很{{mood}}</h2>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
mood: 'happy',
moodlabel: 'normal'
},
methods: {
changeMood() {
this.moodlabel = 'sad'
}
},
})
</script>
- 表达式是数组
数组写法适用于:要绑定多个样式,个数不确定,名字也不确定;
- 表达式是对象
对象写法适用于要绑定多个样式,个数确定,名字也确定,但是动态决定用哪个
- 此外,v-bind:class指令也可与普通的class属性并存,如:
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
条件渲染
条件渲染有两种方式:v-if渲染和v-show渲染
v-if
v-if指令用于条件性地渲染一块内容,这块内容只会在指令的表达式返回truth值的时候被渲染
当然和if一样,也可以添加‘else’块v-else,v-else-if等模块;v-else元素必须紧跟在带v-if或者v-else-if的元素的后面,否则它将不会被识别(成对)。v-else-if也一样,要搭配成对;
<div id="root">
<div v-if="1===1">你好</div>
<div v-else>你不好</div> // 只显示你好
</div>
知识点:在<template>元素上使用v-if条件渲染分组
因为v-if是一个指令,所以必须将它添加到一个元素上,若要切换多个元素,则需要一个元素当做包裹元素,若用div等其它容器,则在渲染时会多出一个无用容器,如下代码,
<div id='root'>
<h2>当前的n值是: {{n}}</h2>
<button @click="n++">点我进行加一操作</button>
<div v-if="n===1">
<h2>你好</h2>
<h2>世界</h2>
<h2>和world</h2>
</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
selectA: false,
n: 1
}
})
</script>
渲染后如下:
会有一个div容器;
基于此,可使用template元素当做不可见的包裹元素,最终渲染的结果将不包含<template>元素
<div id='root'>
<h2>当前的n值是: {{n}}</h2>
<button @click="n++">点我进行加一操作</button>
<template v-if="n===1">
<h2>你好</h2>
<h2>世界</h2>
<h2>和world</h2>
</template>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
selectA: false,
n: 1
}
})
</script>
则只会渲染被包裹的元素,不会增加结构;
注意:template只能与v-if配合使用,不能与v-show配合使用;
v-show
v-show用法与v-if大致一样,只不过不支持v-else和template
<div id='root'>
<h2>当前的n值是: {{n}}</h2>
<button @click="n++">点我进行加一操作</button>
<div v-show="n===1">
<h2>你好</h2>
<h2>世界</h2>
<h2>和world</h2>
</div>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
selectA: false,
n: 1
}
})
</script>
v-if和v-show的区别
v-if是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;
v-if也是惰性的,如果在初始渲染时条件为假,则什么也不做---直到条件第一次变为真时,才会开始渲染条件块;
相比之下,v-show就简单的多---不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS进行切换
即v-if有更高的切换开销,而v-show有更高的初始渲染开销,若需要非常频繁的切换,则使用v-show更好,若在运行时条件很少改变,则使用v-if较好
列表渲染
我们可以用v-for指令基于一个数组来渲染一个列表。v-for指令需要使用item in items形式的特殊语法,其中items是源数据数组(字符串、对象等),而item则是被迭代的数组元素的别名;
遍历数组
<ul id="root">
<li v-for="item in items" :key="item.message">
{{ item.message}}
</li>
</ul>
<script>
var example1 = new Vue({
el: "#root",
data: {
items: [{
message: "Foo"
}, {
message: "Bar"
}]
}
})
</script>
在v-for块中,可访问所有父作用域的property。v-for还支持一个可选的第二个参数index,即当前项的索引;
<ul id="root">
<li v-for="(item, index) in items" :key="item.message">
{{ item.message}}- {{index}}
</li>
</ul>
<script>
var example1 = new Vue({
el: "#root",
data: {
items: [{
message: "Foo"
}, {
message: "Bar"
}]
}
})
</script>
当然,也可以用of代替in,因为它更接近于Js迭代器的语法;
遍历对象
以上示例为遍历一个数组,当然,也可以用v-for来遍历一个对象的属性;
<ul id="root">
<li v-for="value in object">
{{ value }}
</li>
</ul>
<script>
var example1 = new Vue({
el: "#root",
data: {
items: [{
message: "Foo"
}, {
message: "Bar"
}],
object: {
dog: '汪汪',
cat: "喵喵",
cow: "哞哞"
}
}
})
</script>
还有第二个参数,name即属性名(键名),第三个参数,index即索引值
<ul id="root">
<li v-for="(value, name, index) in object">
{{ value }}- {{name}} - {{index}}
</li>
</ul>
<script>
var example1 = new Vue({
el: "#root",
data: {
items: [{
message: "Foo"
}, {
message: "Bar"
}],
object: {
dog: '汪汪',
cat: "喵喵",
cow: "哞哞"
}
}
})
</script>
遍历字符串
<ul id="root">
<li v-for="(value, index) in str">
{{ value }}- {{index}}
</li>
</ul>
<script>
var example1 = new Vue({
el: "#root",
data: {
str: 'study'
}
})
</script>
遍历指定次数
<ul id="root">
<li v-for="(value, index) in 5">
{{ value }}- {{index}}
</li>
</ul>
<script>
var example1 = new Vue({
el: "#root",
data: {
items: [{
message: "Foo"
}, {
message: "Bar"
}]
</script>
在template上使用v-for
类似于v-if,也可以用带有v-for的<template>来循环渲染一段包含多个元素的内容,
<ul id="root">
<template>
<li v-for="(value, index) in object">
{{ value }}- {{index}}
</li>
</template>
</ul>
<script>
var example1 = new Vue({
el: "#root",
data: {
items: [{
message: "Foo"
}, {
message: "Bar"
}],
object: {
dog: '汪汪',
cat: "喵喵",
cow: "哞哞"
},
str: 'study'
}
})
</script>
用key管理可复用的元素
Vue会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染,所以要用key作为数据的唯一表示,但是key不能盲目选择index,理由如下:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实dom更新===虽然界面效果没问题,但是效率低;
- 若结构中还包含输入类的DOM,如input,则会产生错误DOM更新,导致界面出现问题;
在vue中key有什么作用?
- 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当状态中的数据发生变化时,vue会根据新数据生成新的虚拟DOM,随后vue进行新虚拟DOM和旧虚拟DOM的差异比较;
- 比较规则
若旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 若虚拟DOM中内容没变,则直接使用之前的真实DOM;
- 若虚拟DOM中内容变了,则生成新的真实DOM,并替换掉页面中之前的真实DOM
若旧虚拟DOM中未找到与新虚拟DOM相同的key:
- 创建新的真实DOM,随后渲染到新页面
- 开发中如何选择key?
- 最好使用每条数据的唯一标识作为key,如id,手机号,身份证号、学号等唯一值;
- 若不存在对数据的逆序添加,逆序删除等破坏顺序操作,仅用于渲染列表用于展示,则使用index作为key是没有问题的
列表过滤
显示一个数组经过过滤后的版本,不实际变更或重置原始数据,
- 使用侦听器
<ul id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keywords">
<ul>
<li v-for="p in newPersons" :key="p.id">
{{p.name}} - {{p.age}} - {{p.sex}}
</li>
</ul>
</ul>
<script>
var example1 = new Vue({
el: "#root",
data: {
keywords: '', // 输入框绑定查询内容
newPersons: [], // 过滤后的新数组
persons: [{
id: '01',
name: "罗云熙",
age: 18,
sex: '男'
}, {
id: '02',
name: "罗大佑",
age: 28,
sex: '男'
}, {
id: '03',
name: "杨紫",
age: 19,
sex: '女'
}, {
id: '04',
name: "刘紫衫",
age: 21,
sex: '女'
}]
},
watch: {
keywords: {
immediate: true, // 保证一开始先查一遍,此时keywords为空,查询出来是全部数据
// watch里面有两个参数,一个是变化后的数值,一个是变化之前的数值
handler(val) {
this.newPersons = this.persons.filter((p) => {
// p为数组中的每个元素
return p.name.indexOf(val) !== -1
})
}
}
}
})
</script>
- 使用计算属性
<ul id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keywords">
<ul>
<li v-for="p in filPersons" :key="p.id">
{{p.name}} - {{p.age}} - {{p.sex}}
</li>
</ul>
</ul>
<script>
var example1 = new Vue({
el: "#root",
data: {
keywords: '', // 输入框绑定查询内容
newPersons: [], // 过滤后的新数组
persons: [{
id: '01',
name: "罗云熙",
age: 18,
sex: '男'
}, {
id: '02',
name: "罗大佑",
age: 28,
sex: '男'
}, {
id: '03',
name: "杨紫",
age: 19,
sex: '女'
}, {
id: '04',
name: "刘紫衫",
age: 21,
sex: '女'
}]
},
computed: {
filPersons() {
return this.persons.filter((p) => {
return p.name.indexOf(this.keywords) !== -1
})
}
}
})
</script>
从此例子中也可以看出,当watch和computed都能实现功能时,computed更方便~
列表排序
需要显示数组经过排序后的版本,而不实际变更或重置原始数据;
<ul id="root">
<h2>人员列表</h2>
<button @click="sortType=2">升序</button>
<button @click="sortType=1">降序</button>
<button @click="sortType=0">原顺序</button>
</br>
<input type="text" placeholder="请输入名字" v-model="keywords">
<ul>
<li v-for="p in filPersons" :key="p.id">
{{p.name}} - {{p.age}} - {{p.sex}}
</li>
</ul>
</ul>
<script>
var example1 = new Vue({
el: "#root",
data: {
keywords: '', // 输入框绑定查询内容
newPersons: [], // 过滤后的新数组
sortType: 0, // 0表示原顺序 1表示降序 2表示升序
persons: [{
id: '01',
name: "罗云熙",
age: 18,
sex: '男'
}, {
id: '02',
name: "罗大佑",
age: 28,
sex: '男'
}, {
id: '03',
name: "杨紫",
age: 19,
sex: '女'
}, {
id: '04',
name: "刘紫衫",
age: 21,
sex: '女'
}]
},
computed: {
filPersons() {
const filArr = this.persons.filter((p) => {
return p.name.indexOf(this.keywords) !== -1
})
if (this.sortType) {
filArr.sort((p1, p2) => {
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
})
}
return filArr
}
}
})
</script>
对象、数组变更
当对象或数组元素发生变化时,vue是如何监视到他们的变化,从而重新渲染页面的呢?
对于对象来说,会为data中每个层次的数据设置setter函数,并在new Vue时就传入要监测的数据,只要监视的数据发生变化,则就会触发setter函数;但是,对于对象中后来追加的属性,Vue默认不做响应式处理;如确需给后添加的属性做响应式,则需要使用如下API:
- Vue.set(target, key, val)或vm.$set(target,key,val)其中target是要给谁追加,key是追加的属性,val是追加的属性值;
对于数组来说,Vue会对侦听的数组的变更方法(包括push/pop/shift/unshift/splice/sort/reverse)进行包裹,本质上是做了两件事:
- 调用原生对应的方法对数组进行更新
- 重新解析模板,进而更新页面
当然上述数组方法均变更了原始数组,对于非变更数组方法,如filter/concat/slice等,会返回一个新数组,针对非边变更方法,可采用新数组替换旧数组方法实现数据更新监测;
更外,变更数组也可使用Vue.set或vue.$set方法,方法同上述一样;
特别注意:Vue.set和vue.$set都不能直接给vm或vm的根数据对象(_data)添加属性!
<ul id="root">
<h2>人员列表</h2>
<button @click="addAtt">添加属性</button>
</br>
</ul>
<script>
var example1 = new Vue({
el: "#root",
data: {
},
methods: {
addAtt() {
this.$set(this, 'ename', 'Lily')
}
}
})
</script>
会报如下错误:
更多推荐
所有评论(0)