🥔:想只有苦难,做才有答案
更多Vue知识请点击——Vue.js
VUE2-Day3
- 绑定样式
- 1、class绑定
- 2、绑定style样式
- 条件渲染
- 1、v-show
- 2、v-if
- 条件渲染案例
- 列表渲染
- 1、v-for
- 2、key的作用与原理(重要)
- 面试题:react、vue中的key有什么作用?(key的内部原理)
- 案例:给数组头部添加一个人
- 列表过滤:实现模糊查询
- 1、使用watch实现
- 2、使用computed实现
- 列表排序
- Vue监测数据原理及其方法
- 1、更新数据时的一个问题
- 2、模拟Vue监视data对象中的数据
- 3、Vue监测对象中的数据
- (1)怎么监测对象中的数据?
- (2)`Vue.set()`的使用
- 4、Vue监测数组中的数据
- 如何监测数组中的数据?
- 5、回头看第一个
- 监测数据综合案例
绑定样式
1、class绑定
写法:class=“xxx”
xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
数组写法适用于:要绑定多个样式,但是不确定绑定哪几个,就写成数组,省的一个一个绑定。
对象写法适用于:要绑定多个样式,而且要动态决定用不用,这里面可以写一些判断的逻辑。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>绑定class样式</title>
<!-- 引入vue.js文件 -->
<script type="text/javascript" src="../js/vue.js"></script>
<!-- 设置css样式 -->
<style>
.basic {
width: 400px;
height: 100px;
border: solid 1px black;
margin-top: 50px;
}
.happy {
background-color: pink;
}
.normal {
background-color: greenyellow;
}
.sad {
background-color: grey;
}
.style1 {
background-color: skyblue;
}
.style2 {
font-size: 28px;
}
.style3 {
text-align: center;
}
</style>
</head>
<body>
<div id="root">
<!-- :是v-bind:的简写 -->
<!-- 1. 绑定class样式--字符串写法,适用于样式的类名不确定,需要动态指定-->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div>
<!-- 2. 绑定class样式--数组写法,适用于要绑定的样式个数不确定,名字也不确定-->
<div class="basic" :class="classArr" @click="removeStyle">{{name}}</div>
<br />
<!-- 3. 绑定class样式--对象写法,适用于要绑定的样式个数确定,名字也确定,要动态决定用不用-->
<div class="basic" :class="classObj">{{name}}</div>
<br />
<button @click="useStyle1">点击添加样式1</button>
<button @click="useStyle2">点击添加样式2</button>
</div>
<script type="text/javascript">
const vm = new Vue({
el: "#root",
data: {
name: "我是最棒滴!!!",
mood: "normal",
classArr: ["style1", "style2", "style3"],
classObj: {
style1: false,
style2: false,
},
},
methods: {
changeMood() {
const arr = ["happy", "normal", "sad"];
const index = Math.floor(Math.random() * 3);
this.mood = arr[index];
},
//点击去掉第二个框的style样式
//由于pop()是删掉最后一个元素,所以多次点击依次删除style3->style2->style1
removeStyle() {
this.classArr.pop();
},
//点击添加样式1
useStyle1() {
this.classObj.style1 = true;
},
//点击添加样式2
useStyle2() {
this.classObj.style2 = true;
},
},
});
</script>
</body>
</html>
- 可以拿此例子自己敲敲,多到浏览器运行调试看看
2、绑定style样式
:style="{fontSize: xxx + 'px'}"
其中xxx是动态值。
:style="[a,b]"
其中a、b是样式对象。
对象写法比较常用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>绑定style样式</title>
<!-- 引入vue.js文件 -->
<script type="text/javascript" src="../js/vue.js"></script>
<!-- 设置css样式 -->
<style>
.basic {
width: 400px;
height: 100px;
border: solid 1px black;
margin-top: 50px;
}
</style>
</head>
<body>
<div id="root">
<!-- 绑定style样式-对象写法 -->
<div class="basic" :style="styleObj1">{{name}}</div>
<!-- 绑定style样式-数组写法 -->
<div class="basic" :style="[styleObj1,styleObj2]">{{name}}</div>
<div class="basic" :style="styleArr">{{name}}</div>
</div>
<script type="text/javascript">
const vm = new Vue({
el: "#root",
data: {
name: "我是最棒的!!!",
styleObj1: {
fontSize: "40px",
backgroundColor: "pink",
},
styleObj2: {
color: "red",
},
styleArr: [
{
fontSize: "40px",
backgroundColor: "blue",
},
{
color: "yellow",
},
],
},
});
</script>
</body>
</html>
条件渲染
1、v-show
写法:v-show=“表达式”
适用于:切换频率较高的场景。(不会动DOM树,只是隐藏,相当于添加display:none)
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。这是因为v-if会一不小心把标签直接从页面上干掉,而v-show不会干掉,只会隐藏。
2、v-if
写法:
(1).v-if=“表达式”
(2).v-else-if=“表达式”
(3).v-else=“表达式”
适用于:切换频率较低的场景。(因为会动DOM树,节点删来删去不太好)
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。
条件渲染案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>条件渲染</title>
<!-- 引入vue文件 -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 使用v-show做条件渲染:v-show值为true时显示,为false时隐藏(display: none) -->
<h1 v-show="true">你好呀,{{name}}</h1>
<h1 v-show="1===1">你好呀,{{name}}</h1>
<!-- 使用v-if做条件渲染,v-if="false"时会彻底删除标签-->
<h1 v-if="true">再见,{{name}}</h1>
<h1 v-if="1===3">再见,{{name}}</h1>
<!-- 实现功能:随着n递增展示不同的div -->
<h2>{{n}}</h2>
<button v-on:click="n++">点击n+1</button>
<!-- 这里的v-if,v-else-if,v-else和基础js里的一样儿 -->
<div v-if="n === 1">Angular</div>
<div v-else-if="n === 2">React</div>
<div v-else-if="n === 3">Vue</div>
<div v-else>potato</div>
<!-- v-if和template的配合使用(v-show不行)
template不会影响页面结构,页面运行后会自动去掉,但是可以配合v-if控制多个元素整体显示
而且不会影响css拿节点-->
<template v-if="n === 1">
<div>鱿鱼西一号</div>
<div>鱿鱼西二号</div>
<div>鱿鱼西三号</div>
</template>
</div>
<script type="text/javascript">
const vm = new Vue({
el: "#root",
data: {
name: "鱿鱼西",
n: 0,
},
});
</script>
</body>
</html>
列表渲染
1、v-for
-
用于展示列表数据
-
语法:
v-for=“(item, index) in xxx” :key=“yyy”
,其中xxx是遍历的目标,yyy是唯一的索引,用于区分每个嘎达 -
可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
-
遍历数组的话,index是索引值(唯一),p是数组每个元素
-
遍历对象的话,index就是属性名(唯一),p是属性值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>基本列表渲染</title>
<!-- 引入vue文件 -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
<!-- 遍历数组:遍历数组的话,index是索引值,p是数组每个元素 -->
<h2>人员列表(遍历数组)</h2>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{index}}
</li>
</ul>
<!-- 遍历对象:遍历对象的话,index就是属性名,p是属性值 -->
<h2>汽车信息(遍历对象)</h2>
<ul>
<li v-for="(p,index) of car" :key="index">{{p}}-{{index}}</li>
</ul>
<!-- 遍历字符串:遍历字符串的话,index就是索引值,p是每个字符 -->
<h2>遍历字符串</h2>
<ul>
<li v-for="(p,index) of str" :key="index">{{p}}-{{index}}</li>
</ul>
<!-- 遍历指定次数:遍历指定次数的话,index就是索引值,p是从1开始数 -->
<h2>遍历指定次数</h2>
<ul>
<li v-for="(p,index) of 5" :key="index">{{p}}-{{index}}</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: "#root",
data: {
persons: [
{ id: "001", name: "张三", age: 18 },
{ id: "002", name: "李四", age: 19 },
{ id: "003", name: "王五", age: 20 },
],
car: {
name: "马沙拉弟",
price: "10块钱",
color: "骚粉色",
},
str: "hello",
},
});
</script>
</body>
</html>
2、key的作用与原理(重要)
ey可以有三种情况:写唯一标识、写index、不写。其中唯一标识最合适,如果不写会默认是写index。
面试题:react、vue中的key有什么作用?(key的内部原理)
1、 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较(diff算法),比较规则如下:
2、对比规则:
(1)旧虚拟DOM中找到了与新虚拟DOM相同的key:
① 若虚拟DOM中内容没变, 直接使用之前的真实DOM!
② 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2)旧虚拟DOM中未找到与新虚拟DOM相同的key,创建新的真实DOM,随后渲染到到页面。
3、 用index作为key可能会引发的问题:
(1)若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 => 界面效果没问题, 但效率低。
(2) 如果结构中还包含输入类的DOM:
会产生错误DOM更新 => 界面有问题。
4、 开发中如何选择key?:
(1)最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>key的原理和作用</title>
<!-- 引入vue文件 -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
<!-- 遍历数组:遍历数组的话,index是索引值,p是数组每个元素 -->
<h2>人员列表(遍历数组)</h2>
<button @click="add">点击添加老六</button>
<ul>
<!-- index-索引号,p.id-唯一标识符 -->
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{index}}
<input type="text" />
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: "#root",
data: {
persons: [
{ id: "001", name: "张三", age: 18 },
{ id: "002", name: "李四", age: 19 },
{ id: "003", name: "王五", age: 20 },
],
},
methods: {
add() {
const p = { id: "004", name: "老六", age: 19 };
// unshift():此方法的作用是将指定元素添加到数组的开头
this.persons.unshift(p);
},
},
});
</script>
</body>
</html>
案例:给数组头部添加一个人
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>key的原理和作用</title>
<!-- 引入vue文件 -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备一个容器 -->
<div id="root">
<!-- 遍历数组:遍历数组的话,index是索引值,p是数组每个元素 -->
<h2>人员列表(遍历数组)</h2>
<button @click="add">点击添加老六</button>
<ul>
<!-- index-索引号,p.id-唯一标识符 -->
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{index}}
<input type="text" />
</li>
</ul>
</div>
<script type="text/javascript">
new Vue({
el: "#root",
data: {
persons: [
{ id: "001", name: "张三", age: 18 },
{ id: "002", name: "李四", age: 19 },
{ id: "003", name: "王五", age: 20 },
],
},
methods: {
add() {
const p = { id: "004", name: "老六", age: 19 };
// unshift():此方法的作用是将指定元素添加到数组的开头
this.persons.unshift(p);
},
},
});
</script>
</body>
</html>
遍历列表时key的作用(index作为key):
遍历列表时key的作用(唯一标识作为key):
列表过滤:实现模糊查询
1、使用watch实现
handler
函数必须在keyword被改变时才执行,也就是说页面上来还是没有信息的,必须手动输入个东西再删掉(手动改变keyword),才能执行handler
函数,才能显示所有人物的信息,所以必须加个属性immediate: true
,才能默认初始化先调用handler,这样就能实现上来就显示所有人物的信息(最开始keyword=''
)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>列表过滤</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入关键字" v-model="keyWord" />
<ul>
<li v-for="(p,index) of filPersons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
// 用监视(侦听)属性watch实现
new Vue({
el: "#root",
data: {
keyWord: "",
persons: [
{ id: "001", name: "马冬梅", age: 18, sex: "女" },
{ id: "002", name: "周冬雨", age: 19, sex: "女" },
{ id: "003", name: "周杰伦", age: 18, sex: "男" },
{ id: "004", name: "温兆伦", age: 18, sex: "男" },
],
filPersons: [],
},
watch: {
keyWord: {
//页面上来由于filPersons是空,不会显示数据,想要让页面初始化就显示所有人,就要加个immediate: true
//这样就可以让handler函数初始化时先调用一次,由于开始keyword=''
// 而字符串里都包含空字符串,就可以先筛选出来,初始化所有人物信息
immediate: true,
handler(newVal, oldVal) {
this.filPersons = this.persons.filter((p) => {
//判断keyword变化后的新值在不在每个对象的name中,并返回一个新的数组
return p.name.indexOf(newVal) !== -1;
});
},
},
},
});
</script>
</body>
</html>
2、使用computed实现
使用计算属性可以解决watch中的一些繁琐写法,也不用在data中再新定义一个空数组newPersons。
计算属性和监视属性最大的区别就是handler函数是被监视的属性更改时执行,而get是属性被读取时就执行,所以页面加载时filPersons被读取直接就调用get函数返回了一个被筛选后的新数组(条件是name包含空字符串),之后每次keyword改动都会导致Vue模板重新解析,get重新调用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>列表过滤</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入关键字" v-model="keyWord" />
<ul>
<li v-for="(p,index) of filPersons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
//用计算属性computed实现;
new Vue({
el: "#root",
data: {
keyWord: "",
persons: [
{ id: "001", name: "马冬梅", age: 18, sex: "女" },
{ id: "002", name: "周冬雨", age: 19, sex: "女" },
{ id: "003", name: "周杰伦", age: 18, sex: "男" },
{ id: "004", name: "温兆伦", age: 18, sex: "男" },
],
},
computed: {
filPersons() {
return persons.filters((p) => {
return p.name.indexOf(this.keyWord) !== -1;
});
},
},
});
</script>
</body>
</html>
列表排序
先过滤,再排序
首先定义一个sortType
属性来判断排序的种类,然后在计算属性里做一个判断。
注意这里仔细理解理解数组的sort
方法,很奇怪。
这个案例再次体现出计算属性的强大之处,只要get里用到的数据(keyword,sortType)发生改变,get都会重新执行,模板都会重新解析,这个业务逻辑就实现了
先复习一下数组的排序sort
升序降序方法
let arr = [1, 5, 3, 7, 4];
//降序b-a
// arr.sort((a, b) => {
// return b - a;
// });
// console.log(arr);
//升序a-b
arr.sort((a, b) => {
return b - a;
});
console.log(arr);
例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>列表排序</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h1>人员列表</h1>
<input type="text" placeholder="请输入关键字" v-model="keyword" />
<button @click="sortType = 0">原顺序</button>
<button @click="sortType = 1">年龄降序</button>
<button @click="sortType = 2">年龄升序</button>
<ul>
<li v-for="(p,index) in newPersons" :key="p.id">
{{p.name}}----{{p.age}}----{{p.sex}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#root",
data: {
sortType: 0, //0原顺序,1年龄降序,2年龄升序
keyword: "",
persons: [
{ id: "001", name: "马冬梅", age: 18, sex: "女" },
{ id: "002", name: "周冬雨", age: 19, sex: "女" },
{ id: "003", name: "周杰伦", age: 30, sex: "男" },
{ id: "004", name: "温兆伦", age: 50, sex: "男" },
],
},
computed: {
newPersons() {
//先过滤,再排序
const arr = this.persons.filter((ele) => {
return ele.name.includes(this.keyword);
});
// 或者if(this.sortType)
if (this.sortType !== 0) {
arr.sort((a, b) =>
this.sortType === 1 ? b.age - a.age : a.age - b.age
);
}
return arr;
},
},
});
</script>
</body>
</html>
Vue监测数据原理及其方法
1、更新数据时的一个问题
如果我要点击按钮实现更新马冬梅的信息,那么如果一个属性一个属性地改,可以修改成功,并且Vue也会检测到并更新到页面。但是我如果直接把整个对象改了,可以修改成功,但是Vue监测不到并且不更新到页面,这是为什么?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>列表排序</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h1>人员列表</h1>
<button @click="updateMei">更新马冬梅信息</button>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}----{{p.age}}----{{p.sex}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#root",
data: {
persons: [
{ id: "001", name: "马冬梅", age: 18, sex: "女" },
{ id: "002", name: "周冬雨", age: 19, sex: "女" },
{ id: "003", name: "周杰伦", age: 30, sex: "男" },
{ id: "004", name: "温兆伦", age: 50, sex: "男" },
],
},
methods: {
updateMei() {
// this.persons[0].name = "马老师"; //奏效(页面和Vue都监测到了)
// this.persons[0].age = 28; //奏效(页面和Vue都监测到了)
// this.persons[0].sex = "男"; //奏效(页面和Vue都监测到了)
//此处Vue监测不到数据改变,但是实际上已经改了
this.persons[0] = { id: "001", name: "马老师", age: 28, sex: "男" };
},
},
});
</script>
</body>
</html>
2、模拟Vue监视data对象中的数据
用原生js写一个Vue监视data数据改变的效果
可以看出,Vue监视数据的原理,就是靠setter
如果不理解obj[k]
,想想前边讲Object.defineProperty
时那个number
getter:当属性值被访问时,会触发getter方法,这时Vue会将当前的Watcher对象(即订阅者)存储起来,用于后续数据变化时的更新操作。
setter:属性被修改–>调用setter–>重新解析模板–>生成新的虚拟DOM–>新旧DOM对比(diff)–>更新页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script type="text/javascript">
let data = {
name: "小猪佩奇",
age: 5,
};
//创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data);
console.log(obs);
//准备一个vm实例对象
let vm = {};
vm._data = data = obs;
function Observer(obj) {
//1.创建一个数组接收传入对象的属性名
const keys = Object.keys(obj);
//2.遍历属性名,让Observer实例对data中的每个数据进行数据代理
keys.forEach((k) => {
Object.defineProperty(this, k, {
get() {
//有人想读实例中的属性值,我就把data中对应的属性值拿过来
return obj[k];
},
set(val) {
console.log(
`${k}被改了,我要去解析模板,生成虚拟DOM...我要开始忙了`
);
//有人想改实例中的属性值,我就把data中对应的属性值更改(数据代理)
obj[k] = val;
},
});
});
}
</script>
</body>
</html>
这只是一个简单的模拟,实际上Vue做了更多事情,比如:
1、在setter中不是简单的输出,而是重新解析模板,生成虚拟DOM,新旧虚拟DOM对比,更新页面实现响应式。
2、Vue中vm还对_data
做了数据代理,可以直接vm.name
,不用vm._data.name
3、上面的简单模拟例子,如果数据是套娃的(对象中还有对象还有对象),就监测不到。但是Vue就可以,不管你数据藏得多深,Vue都能找到,每个数据都有自己的getter和setter。只要有数据被改,就会实现响应式。
3、Vue监测对象中的数据
(1)怎么监测对象中的数据?
1、vue会监视data中所有层次的数据,不管你藏得有多深。
2、如何监测对象中的数据?
通过setter实现监视,且要在new Vue
时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
(2)Vue.set()
的使用
用法:
向响应式对象中添加一个 property
(属性),并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.girlfriend.sex = '女'
)为啥呢?
_data
在收集data中的数据之前,先做了一个加工(这个加工也可以称之为数据劫持),那就是给每个数据匹配一个getter和setter,前面说了,Vue监视数据的原理,就是靠setter。所以如果我们后期手动直接给_data添加属性(注意区别手动添加和从data中收集),是无法实现响应式的,因为没办法给它setter,只有从data中收集过来的属性才能有setter。
Vue.set()是有局限性的!!!它只能给data中的某个对象追加属性,不能给vm 或 vm的根数据对象(data或_data) 添加属性!!!
也就是说第一个参数不能是 Vue 实例,或者 Vue 实例的根数据对象(_data)
<div id="root">
<h2>我的名字:{{name}}</h2>
<h2>我的年龄:{{age}}</h2>
<!-- v-if当属性值为undefined不显示,也不报错 -->
<h3 v-if="sex">我的性别:{{sex}}</h3>
<button @click="addmySex">点击添加我的性别</button>
<hr>
<h2>她的名字:{{girlfriend.name}}</h2>
<button @click="addherSex">点击添加性别,属性值为女</button>
<h2 v-if="girlfriend.sex">她的性别:{{girlfriend.sex}}</h2> 、
<h2>她的年龄:对外{{girlfriend.age.fakeAge}},真实{{girlfriend.age.realAge}}</h2>
<h2>朋友们</h2>
<ul>
<li v-for="p in girlfriend.friends" :key="p.id">
{{p.name}}----{{p.age}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
name: 'www',
age: 18,
girlfriend: {
name: 'zzz',
// sex: '女',
age: {
realAge: 23,
fakeAge: 18
}
}
},
methods: {
addherSex() {
// Vue.set(this.girlfriend, 'sex', '女');
this.$set(this.girlfriend, 'sex', '女'); //vm.$set(vm.girlfriend, 'sex', '女');
},
addmySex() {
Vue.set(this, 'sex', '男');
}
},
})
</script>
4、Vue监测数组中的数据
如何监测数组中的数据?
Vue定义了一个非常厉害的方法,目的只有一个:实现响应式。
方法里包装的代码本质就是做了两件事:
(1)调用原生对应的方法对数组进行更新。然后第二步在这个基础上再加东西
(2)重新解析模板,生成虚拟DOM,diff算法……进而更新页面,实现响应式。
所以在Vue修改数组中的某个元素实现响应式一定要用如下方法:
(1)使用这些API:push()
在后面追加、pop()
删除最后一个、shift()
删除第一个、unshift()
在前面追加、splice()
在指定位置增删改、sort()
数组排序、reverse()
反转数组,这些api都是可以修改原数组的,可以在(MDN Web Docs (mozilla.org))查看。
(2)Vue.set()
或 vm.$set()
也可以实现响应式,第二个参数写索引,第三个写元素,可以改也可以加
5、回头看第一个
说了这么多,再回头看第一个问题,恍然大悟
1、单独改每个属性可以奏效,是因为Vue可以监测到对象里的每个属性,不管它藏的多深,只要是个对象,就有相应的getter和setter
2、将数组中的元素单独替换,数据修改成功,但是Vue没有监测到,页面不显示,这是因为数组中的每个元素是没有getter和setter的,如果修改数组中的元素无法实现响应式.
3、要想让更改数组中的元素实现响应式,就得调用数组的七个api或者用Vue.set()方法
解决:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>列表排序</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h1>人员列表</h1>
<button @click="updateMei">更新马冬梅信息</button>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}----{{p.age}}----{{p.sex}}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: "#root",
data: {
persons: [
{ id: "001", name: "马冬梅", age: 18, sex: "女" },
{ id: "002", name: "周冬雨", age: 19, sex: "女" },
{ id: "003", name: "周杰伦", age: 30, sex: "男" },
{ id: "004", name: "温兆伦", age: 50, sex: "男" },
],
},
methods: {
updateMei() {
// this.persons[0].name = "马老师"; //奏效(页面和Vue都监测到了)
// this.persons[0].age = 28; //奏效(页面和Vue都监测到了)
// this.persons[0].sex = "男"; //奏效(页面和Vue都监测到了)
//不奏效,此处Vue监测不到数据改变,但是实际上已经改了
// this.persons[0] = { id: "001", name: "马老师", age: 28, sex: "男" };
this.persons.splice(0, 1, {
id: "001",
name: "马老师",
age: 28,
sex: "男",
});
},
},
});
</script>
</body>
</html>
监测数据综合案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>数据监测综合案例</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>学生信息</h1>
<!-- 六个任务如下 -->
<button @click="student.age++">年龄加1</button><br />
<button @click="addSex">增加性别属性,默认值:男</button><br />
<button @click="addFriend">在列表首位添加一个朋友</button><br />
<button @click="updateFirstFriendName">
修改第一个朋友的名字为:小张,年龄改为20</button
><br />
<button @click="addHobby">添加一个爱好</button><br />
<button @click="updateHobby">修改一个爱好为:开车</button><br />
<h3>姓名:{{student.name}}</h3>
<h3>年龄:{{student.age}}</h3>
<h3 v-if="student.sex">性别:{{student.sex}}</h3>
<h3>爱好:</h3>
<ul>
<li v-for="(h,index) in student.hobby" ::key="index">{{h}}</li>
</ul>
<h3>朋友们:</h3>
<ul>
<li v-for="(f,index) in student.friends" ::key="index">
{{f.name}}-{{f.age}}
</li>
</ul>
</div>
<script type="text/javascript">
const vm = new Vue({
el: "#root",
data: {
student: {
name: "小吴",
age: 18,
hobby: ["羽毛球", "乒乓球", "篮球"],
friends: [
{ name: "小黄", age: 19 },
{ name: "小何", age: 19 },
],
},
},
methods: {
addSex() {
Vue.set(this.student, "sex", "男");
//this.$set(this.student, "sex", "男")
},
addFriend() {
this.student.friends.unshift({ name: "小王", age: 18 });
},
updateFirstFriendName() {
// this.student.friends[0].name = "小张";
// this.student.friends[0].age = 20;
// 另一种写法
this.student.friends.splice(0, 1, { name: "小张", age: 20 });
},
addHobby() {
this.student.hobby.push("排球");
},
updateHobby() {
//把第二个修改为开车
this.student.hobby.splice(1, 1, "开车");
//this.$set(this.student.hobby,1,'开车')
},
},
});
</script>
</body>
</html>