0
点赞
收藏
分享

微信扫一扫

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性

需求:点击按钮,更新马冬梅信息

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_数据监测

逐个属性修改

<div id="root">
<h2>{{title}}</h2>
<button @click="updateMei">更新马冬梅信息</button>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.gender}}
</li>
</ul>
</div>

提示:点击按钮,添加绑定事情,修改 persons 信息

new Vue({
el: '#root',
data: {
title: '更新列表信息',
persons: [
{ id: "001", name: "马冬梅", age: 18, gender: "女" },
{ id: "002", name: "周冬雨", age: 19, gender: "女" },
{ id: "003", name: "周杰伦", age: 21, gender: "男" },
{ id: "004", name: "温兆伦", age: 22, gender: "男" },
],
},
methods: {
updateMei(){
this.persons[0].name = '马老师'
this.persons[0].age = 50
this.persons[0].gender = '男'
}
},
});

提示:通过索引,挨个修改属性值;

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_数据监测_02

发现问题

提示:单个属性修改时没有问题;逐行属性修改时,会遇到问题;

methods: {
updateMei(){
this.persons[0]={id:'001',name:'马老师',age:50,gender:'男'}
}
},

看下效果:

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_数据监测_03

这样点完后,“马冬梅”这行数据,却没有更新;

但是控制台输出没有问题:

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_Vue_04

Vue监测对象数据

Vue会对 data 数据进行监测,才能完成响应式;vm._data 里存着 data 的备份, _data 里属性的变化(如:title 变了),会引起 setter 的调用,重新解析模板,生成新页面;

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_Vue_05

提示:title 一改,setter 就调 ,重新解析模板,生成新的虚拟DOM,新旧虚拟DOM对比,更新页面;

模拟监测对象

根据提示,写一个模拟的过程:既然是模拟,就不用引入 vue.js 了;

let data ={
name:'jack',
age:30
}

// 创建一个监视的实例对象,用于监视 data 中的属性的变化
const obs = new Observer(data);
console.log(obs);

// 准备一个 vm 实例对象;
let vm = {}
vm._data = data = obs // 复制副本

// 创建监视构造函数
function Observer(obj){
// 汇总对象中所有的属性形成一个数组
const keys = Object.keys(obj);
keys.forEach((k)=>{
Object.defineProperty(this,k,{
get(){
return obj[k]
},
set(val){
console.log(`${k}被修改了,生成虚拟DOM,要去解析模板...`);
obj[k] = val
}
})
});
}

提示1:创建的构造函数,给实例对象绑上了 getter 和 setter ;通过 ​​Object.defineProperty() 方法​​

提示2:实例化对象后,obs 的属性存取方式使用了 存取器属性,可以参考​​使用对象​​

提示3: ​vm._data = data = obs​这么一连等之后,各对象的属性就联动起来了,即数据代理;

看一下连等前的 data  和  obs :data 是数值描述,obs 是存取描述;

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_添加响应式属性_06

看一下连等之前 data 和 obs 的属性描述符:

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_Vue_07

连等后的vm._data 、data 和 obs 就完全一样:

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_Vue_08

data 的数据变化,obs 也跟着变化,同样的, obs 数据变化, data 也会变化 ;

这里只模拟了单级属性的“联动”,多层级的属性“联动”,没有实现;

这只是模拟 Vue 监测对象类数据的做法,但凡数据改变了,setter 就发挥作用了;

Vue.set 的使用

先写个展示的页面:

<div id="root">
<h2>school:{{school}}</h2>
<h2>address:{{address}}</h2>
<hr>
<h3>uname:{{student.name}}</h3>
<h3>age:{{student.age}}</h3>
<h3>friends</h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}-{{f.age}}
</li>
</ul>
</div>

提示1:列表渲染,​:key=“index”​ ; 

提示2:访问 vm.a ,如果 a 不存在,会报错,如果 a.b ,a 存在 b 不存在的话,不会报错;

const vm = new Vue({
el:'#root',
data:{
school:'51CTO',
address:'51cto.com',
student:{
name:'Jack',
age:30,
friends:[
{name:'Jerry',age:28},
{name:'Tom',age:29}
]
}
}
})

提示1:数据中使用了多级属性;

看下效果:

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_数据监测_09

此时,要后期给学员 "Jack",加上性别属性该怎么做? 

添加数值属性

<h3>gender:{{student.gender}}</h3>

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_数据监测_10

能否在控制台直接添加呢:

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_数据监测_11

添加后并不会立即被渲染到页面上,因为 ​vm._data.student.gender ="male"​ 设置的属性是数据描述符,不是存取描述符(没有 getter 和 setter 存取器)。

添加响应式属性

通过 ​Vue.set(target,key,val)​ 方法,可以给 vm 添加存取器属性,这样添加的属性就具有响应式了;

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_Vue_12

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_添加响应式属性_13

这样一来,gender 属性也就有了响应式:

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_Vue_14

提示:​vm.data​​ 和​vm._data​​ 之间有数据代理的关系,也就是说:​vm.student === vm._data.student​  等价的;

Vue.set(vm._data.student,'gender','male') 
// 等价于
Vue.set(vm.student,'gender','male')

除了 ​Vue.set() 方法,还可以使用 ​vm.$set()​ 方法来实现,这两种方法是一样的:

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_数据监测_15

通过 ​vm.$set()​ 添加的属性就具有响应式了,添加成功后,会立即被渲染到页面上:

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_数据监测_16

Vue.set() 和 vm.$set() 等价

需求:点击按钮,给学员添加性别属性;

<div id="root">
<h2>{{name}}</h2>
<button @click="addGender">点击添加性别:男</button>
<h3>姓名:{{student.name}}</h3>
<h3>年龄:{{student.age}}</h3>
<h3 v-if="student.gender">性别:{{student.gender}}</h3>
<h3>朋友们</h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}-{{f.age}}
</li>
</ul>
</div>

提示1:绑定点击事件,添加 ​v-if​​ 条件渲染,条件是表达式: ​student.gender​ 有值就显示,没值不显示;

const vm = new Vue({
el:"#root",
data:{
name:'学生信息',
student:{
name:"张三",
age:29,
friends:[
{name:'李四',age:30},
{name:'王五',age:32}
]
}
},
methods: {
addGender(){
// Vue.set(this.student,'gender','男')
this.$set(this.student,'gender','男') // vm.$set() 添加响应式属性
}
},
})

提示:通过 ​vm.$set()​ 或 ​Vue.set()​ 方法都可以给对象添加响应式属性;

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_添加响应式属性_17

注意:​Vue.set()​ 和 ​vm.$set()​ 不能直接给 vm 和根添加属性;

Vue2(笔记12) - Vue核心 - 数据监测原理、监测对象数据、添加响应式属性_添加响应式属性_18

比如:要添加学校信息,可以先建一个 school 的属性,把 leader 放在 school 里面;

const vm = new Vue({
el:"#root",
data:{
school:{
leader:"tom",
age:50
},
student:{//...}
}
})

这样就可以给 school 和 student 分别添加响应式属性了;








举报

相关推荐

0 条评论