目录
一、绑定 class 样式
(一)字符串写法
1.流程介绍
适用于样式的类名不确定 需要动态指定
通过 指令绑定的形式,在模板中的类标签绑定 Vue 实例中的数据 mood
<div class="basic" :class="mood" @click="changeMood">test</div>
在实例对象的 data 加入 mood 属性并且 用对应的类名赋值
data: {
name: '一个人',
mood: 'normal'
},
根据以上原理 我们能设计出 一个点击切换样式的效果
点击切换成 happy 类名 中的样式
2.代码实现
<body>
<div id="root">
<div class="basic" :class="mood" @click="changeMood">test</div>
<script>
new Vue({
el: '#root',
data: {
name: '一个人',
mood: 'normal'
},
methods: {
changeMood() {
this.mood = 'happy'
}
},
})
</script>
</div>
</body>
随机显示背景颜色函数设计,把三个类名存到数组中,然后随机取下标,进行读取就行
methods: {
changeMood() {
const arr = ['happy', 'sad', 'normal']
const index = Math.floor(Math.random() * 3)
this.mood = arr[index]
}
},
(二)数组写法
1.流程介绍
适用于绑定的样式个数不确定,名字也不确定
想要随时添加样式删除样式 就在模板中 插值一个数组类型的数据
<div class="basic" :class="arr" @click="changeMood">test</div>
然后在 data 中添加一个数组类型的数据,在数组里面放入我们要展示的类名 ,我们还能通过操作数组进行,增加和删除样式
data: {
name: '一个人',
mood: 'normal',
arr:['normal','happy','bianhua'],
},
删除
vm.arr.shift()
增加
vm.arr.push('增加的类名')
2.代码实现
<body>
<div id="root">
<div class="basic" :class="arr" @click="changeMood">test</div>
</div>
<script>
new Vue({
el: '#root',
data: {
arr:['normal','happy','bianhua'],
},
})
</script>
</div>
</body>
(三)对象写法
1.流程介绍
适用于绑定的样式确定 名字也确定 要动态决定用不用
先绑定对象
<div class="basic" :class="Obj" @click="changeMood">test</div>
在实例对象 vm 中的 data 对象中写一个对象类型数据 里面放着样式类名 通过 true 和 false 来判断使用不使用这个样式
data: {
name: '一个人',
mood: 'normal',
arr:['normal','happy','bianhua'],
Obj:{
happy:true,
bianhua:true
}
},
method
2.代码实现
<body>
<div id="root">
<div class="basic" :class="Obj" @click="changeMood">test</div>
</div>
<script>
new Vue({
el: '#root',
data: {
Obj:{
happy:true,
bianhua:true
}
},
})
</script>
</div>
</body>
二、绑定 style 样式(了解)
除了可以用 class 绑定样式 我们还可以通过内联 style 来绑定样式
(一)对象写法
先绑定对象
<div class="basic" :style="Obj" @click="changeMood">test</div>
在实例对象 vm 中的 data 对象中写一个样式对象,里面的属性就是我们要设置的样式,属性名不能随便写,得遵循 css 中的属性名
Obj: {
fontSize: '40px'
}
(二)数组写法
不常用,如果要写很多的样式 我们能分开写
<div class="basic" :style="Obj" @click="changeMood">test</div>
styleArr:[
{
fontSize: '40px'
},
{
backgroundColor: 'blue'
}
]
三、条件渲染
(一)v-show 渲染
变化频率高的时候使用v-show
隐藏标签的内容,节点还在但是被隐藏了
v-show=‘false’ 内容隐藏 true 就是显示
<h2 v-show="false">欢迎来到{{name}}</h2>
引号里面可以写表达式,还有 vm 中的数据 ,比如 v-show='a' 在 vm 实例的数据中添加 a 的具体值 实现动态的显示和隐藏
还能写 v-show=‘a=1’ 判断布尔值 具体是什么 为真则显示 假则隐藏
(二)v-if 渲染
变化频率低的时候使用v-if 因为很暴力不展示的元素直接删除
1.v-if
判断里面是否为真为真就显示 为假就删除
<h2 v-if="false">欢迎来到{{name}}</h2>
2.v-else-if
和 if 和 elseif 使用方法类似 先判断 if 如果不符合 再依次往下判断 但是一旦为真 就不再判断下面的了,如果同时三个 v-if 则每行语句都需要重新判断。
<div v-if="n===1">n=1</div>
<div v-else-if="n===2">n=2</div>
<div v-else-if="n===3">n=3</div>
3.v-else
和 else 用法差不多 但是后面不要接数值了 ,如果 v-if 和v-else-if 中的语句都为假 就显示 v-else 中的语句
<div v-if="n===1">n=1</div>
<div v-else-if="n===2">n=2</div>
<div v-else-if="n===3">n=3</div>
<div v-else>n是个厉害</div>
4.注意事项
v-if 不允许被打断
如果在v-else-if 中间添加了其他的标签,就不会执行后面的语句。下面只能输出 n=1 和 n=2 其它找不到了
<div v-if="n===1">n=1</div>
<div v-else-if="n===2">n=2</div>
<div>@</div>
<div v-else-if="n===3">n=3</div>
<div v-else>n是个厉害</div>
模板标签 tempalate
如果想让下面几个 h2 标签都在 a=1 时显示出来
我们可能会想到每个标签都加一个 v-if 判断条件的写法 但是太复杂
<h2 v-if="true">你好</h2>
<h2 v-if="true">您好</h2>
<h2 v-if="true">我们好</h2>
我们可以用一个模板 template 来包裹这些都要显示的标签,然后给这个模板一个 v-if=’n===1‘,就可以控制所有的标签的显示了,而且使用模板不会影响标签结构,当展示出来的时候模板会自动脱落,记住不能和 v-show 使用
<template v-if="n === 1 ">
<h2>你好</h2>
<h2>您好</h2>
<h2>我们好</h2>
</template>
四、列表渲染
(一)v-for 遍历
1.遍历数组
数组信息:
persons:[
{
id: '001',
name: '张三',
age: 18
},
{
id: '002',
name: '李四',
age: 19
},
{
id: '003',
name: '王五',
age: 20
},
],
遍历生成相同 的 结构标签 前面一个变量 代表后面的数组每次遍历的当前元素
in 可以换成 of
<li v-for="p in persons">
{{p.name}}-{{p.age}}
</li>
里面最好写上每个生成的 li 标签的 key 值就是每个生成出的标签独特的标识 ,每个生成的 li 的 key 值应该不同 切忌写成 :key='1' 这样每个生成的 li 标签的 key 值相同就会报错,就和身份证不能一样类比。
<li v-for="p in persons" :key='p.id'>
{{p.name}}-{{p.age}}
</li>
其 v-for=‘p in persons’ in 前面可以有两个变量,完整写法是 v-for=‘(p,index) in persons’
括号里面第一个变量代表后面的数组每次遍历的当前元素,第二代表当前元素的下标
所以我们 的 key 值还有第二种写法
<li v-for="(p,index) in persons" :key='index'>
{{p.name}}-{{p.age}}
</li>
2.遍历对象
对象信息:
car:{
name:'小汽车',
price:'100万',
color:'白色'
}
遍历对象
和遍历数组类似,在遍历对象时前面也会收到两个数据 第一个是 value 就是当前属性的属性值
key 是当前属性的属性名
<li v-for="(value,k) in car" :key="k">
{{k}}-{{value}}
</li>
3.遍历字符串 少见
str:'hello'
和前面类似 v-for 里面也会收到两个数据 一个是 当前字符 另一个是当前字符的下标
<li v-for="(char,index) in str" :key="index">
{{char}}-{{index}}
</li>
4.遍历指定次数 特别少见
下面是只当遍历 5 次
number 是从1开始的数字 1 2 3 4 5 index 是从 0 开始的下标 0 1 2 3 4
<li v-for="(number,index) in 5" :key="index">
{{number}}-{{index}}
</li>
(二)v-for 中 key 的原理
1.简单介绍
key 如果不写默认是 下标就是 index
key 的作用就是给节点进行标识 类似于人类的身份证号,在页面中查看真实 dom 结构发现 li 没有 key 属性 因为key 被 Vue 内部使用,当 Vue 把 li 转化成真实 dom 节点时就把 key 这个特殊属性征用了,给 Vue 自己用 不给别人显示,如果换成别的属性名就显示了
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
</li>
2.常见错误
但是如果使用 index 作为 key 的 值使用 一定情况下会发生错误,比如我们想三个人前面加一个王五,但是我们先在每个人后面添加一个文本框,然后输入三个人的信息,然后再添加这个王五,我们发现信息篡位了,具体流程如下:通过虚拟 dom 节点的(diff)对比算法 进行更新
效率较低,因为复用的比较少
我们可以通过使用 id 来作为 key 的值来使用来解决这个问题,数据不会错位也提高了代码的效率
3.总结
1.可能产生的问题:
逆序添加数据,删除数据 破坏顺序的行为 会产生没有必要的真实dom 的更新,界面效果没问题,但是效率低。
如果结构中包含输入类的dom 会产生错误的 dom 更新 界面有问题
2.如何选择 key :
使用每一条数据的唯一标识符为 key ,比如 id 手机号 身份证号唯一值
如果不存在逆序增加 删除 破坏顺序的操作,仅用于渲染列表用于展示,使用 index 作为 key 是没问题的
(三)列表信息过滤
1.使用 watch 监听实现
过滤器返回值是过滤数组的条件
空字符串包含在任何字符串之中,返回值是 0 不是 -1 所以能返回过滤数组
<body>
<div id="root">
<h2>人员列表</h2>
<input type="" placeholder="请输入名字" v-model="keyword">
<ul>
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
keyword: '',
persons: [
{ id: '001', name: '李三', age: 18, sex: '女' },
{ id: '002', name: '李四', age: 19, sex: '男' },
{ id: '003', name: '王四', age: 20, sex: '男' },
],
filPersons:[]
},
watch: {
keyword:{
immediate:true,
handler(val) {
this.filPersons = this.persons.filter((p) => {
return p.name.indexOf(val) !== -1
})
}
}
}
})
</script>
</body>
2.使用 计算属性 监听实现
计算属性中 return 中才是返回的新的数组,不用在 data 中新创建一个空的数组来存最后的数据,不用给数组重新赋值
<body>
<div id="root">
<h2>人员列表</h2>
<input type="" placeholder="请输入名字" v-model="keyword">
<ul>
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
keyword: '',
persons: [
{ id: '001', name: '李三', age: 18, sex: '女' },
{ id: '002', name: '李四', age: 19, sex: '男' },
{ id: '003', name: '王四', age: 20, sex: '男' },
],
},
computed: {
filPersons() {
return this.persons.filter((p) => {
return p.name.indexOf(this.keyword) !== -1
})
}
}
})
</script>
</body>
(四)列表排序
在过滤后的数组中进行操作,只要里面任何属性发生变化,就会重新调用
<body>
<div id="root">
<h2>人员列表</h2>
<input type="" 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) in filPersons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
keyword: '',
persons: [
{ id: '001', name: '李三', age: 18, sex: '女' },
{ id: '002', name: '李四', age: 50, sex: '男' },
{ id: '003', name: '王四', age: 30, sex: '男' },
],
sortType: 0,
},
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>
</body>
五、Vue 检测数据原理
Vue 监测 data 中所有层次的数据
(一)问题提出
很重要,我们把 data 中的数据更改 vue 会有一个内置的watch监测,然后在页面中更改新的数据,这节我们得明白不是所有的,更改的情况我们 vue 都能监测并认可更改的,有的时候可能数据变了,但是vue 监测不到,数据不会更改
如下 我们想对数组对象中的第一对象中的属性进行修改,使用下面我们构造的方法,挨个属性进行修改,能成功实现更改第一个对象的属性,并成功被 vue 监测到 新的数据在模板中能成功显现出来
methods: {
updataZhang() {
this.persons[0].name = '李一百'
this.persons[0].age = 20
this.persons[0].sex = '男'
}
},
但是一旦我们换一个方法,我们使用一个新的对象来替换原来的对象的方法,就不能被 vue 监测到数据的变化,在页面中显示的还是原来被替换前的数据,但是数据确实被改变了,只是vue 无法监测而无法改变。这个问题是怎么回事? 稍后我们再解答
methods: {
updataZhang() {
this.persons[0] = { id: '001', name: '李一百', age: 20, sex: '男' }
}
},
(二)检测对象
通过 setter 实现监视 在 new 时就传入要检测 的数据,后面有如何追加新数据
1.数据代理回顾
数据代理过程如下图,其实应该有两个过程 如黄字所写,应该先加工一下 data , _data
中是加工后的 data 的值。
_data 里面是加工过后的 data 的内容 通过 getter 方法来获得 name 值 通过 setter 方法来修改name 值,所以_data 里面每一个元素都被加上了一个 getter 和 setter 方法,几个属性就几个getter setter
加工的意义是给 _data 变成响应式的数据,数据变页面也跟着变就是响应式,修改_data 中的属性引起 setter 方法的调用,setter 里面写了一个重新解析模板的内容 然后页面就更新了
2.监测过程
vue 监测对象中数据的变化就是通过 set 函数监测,vue 会将 data 中所有的数据都解析出来包括对象里面的每一个元素,对象中嵌套的对象里面的元素也能解析出来,并给每一个元素都配置一个 setter getter 方法,如果数据某个数据被更改,就会调用对应的 getter 方法,方法里面的语句是重新解析 vue 模板并更新显示模板,所以页面会更新。
3.实例说明
data: {
name: '学校',
address: '北京',
student: {
name: 'tom',
age: {
rage: 20,
sage: 21
},
friends: [
{ name: 'jerry', age: 35 },
{ name: 'tony', age: 36 }
]
}
},
如果想在对象 data 中的 student 对象中新增加一个属性 sex 并在页面中显示出来,
我们不能直接 vm.sex 这个行为很离谱 本来 vm 中的属性 name 这些都是数据代理来的 而我们想直接加一个 vm.sex 就没setter getter 方法 就不能更新
我们也不能 在 _data 中加一个 vm._data.student.sex,因为我们没经过 data 阶段的加工就进行增加 没有分配的 getter 和 setter,也不能更新
那么怎么追加响应式的属性呢? 我们可以使用 vue 提供的一个 API 来添加一个响应式的属性
Vue.set(vm._data.student,'sex','男')
括号里面是 在哪里追加, 追加的属性,和追加的属性值
vm.$set(vm._data.student,'sex','男') 同理 在vm 中追加
vm._data.student 等同于 vm.student 在数据代理中学过
Vue.set(vm.student,'sex','男')
这个方法只能给 data 数据中的 对象添加数据 不能直接给 data 和 Vue添加数据,这是不被 Vue 允许的
(三)检测数组
通过包裹数组更新元素的方法实现
1.监测过程
数组不是通过 setter getter 来监测数组内元素的变化的,不像对象那样里面每个属性都有一个getter setter,来监测变化 并重新在页面显示出来
而是当执行上面这些行为时 数组被改变了(过滤不影响原数组,要把过滤后的新数组赋给原数组就变了)vue 能监测到并能改变也页面的显示,使用了包装的方法
2.实例说明
为什么监测到这些行为 这些已经不是 Array 原型对象上的 方法了,而是 Vue 给我们包装的方法
拿push 举例
第一步 还是调用一下原来的 push 方法
第二步 重新解析模板 生成虚拟 dom 显示页面
Vue.set 方法也能对数组起效果,但不能给 vm 或 vm 的根对象加属性
Vue.set(vm._data.student,'sex','男')
括号里面是 在哪里追加,修改的元素下标,和追加的数组值
记得数据代理的知识 前面用到的 vm._data.student 可以简写成 vm.student
(四)数据劫持
把原本的数据变成能被监测的形式,就是当数据改变时 setter 就会劫持数据并重新解析渲染页面,是一个行为 。
(五)完整代码
这里是一个完整的练习代码可以看看加深理解
<body>
<div id="root">
<h1>学校信息</h1>
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<h1>学生信息</h1>
<button @click="addSex">添加一个性别</button>
<h2>姓名:{{student.name}}</h2>
<h2>性别:{{student.sex}}</h2>
<h2>年龄:{{student.age.rage}} :对外:{{student.age.sage}}</h2>
<h2>盆友们</h2>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<h2>盆友们</h2>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}---{{f.age}}
</li>
</ul>
</div>
<script>
new Vue({
el: '#root',
data: {
name: '学校',
address: '北京',
student: {
name: 'tom',
age: {
rage: 20,
sage: 21
},
hobby:['抽烟','喝酒','烫头'],
friends: [
{ name: 'jerry', age: 35 },
{ name: 'tony', age: 36 }
]
}
},
methods: {
addSex() {
Vue.set(this.student, 'sex', '男')
}
},
})
</script>
</body>