- 一个物体能对外界的刺激作出反应,那他就是响应式的
-
- const vm = new Vue({data:{n:0}})
- 我如果修改 vm.n ,那么UI中的 n 就回来响应我
- Vue通过Object.defineProperty来实现数据响应式
- Vue通过Object.defineProperty的 getter/setter 对收集的依赖项进行监听,在属性被访问和修改时通知变化,进而更新视图数据
getter/setter
用于对属性的读写进行监控
getter
let obj1 = {
姓: "高",
名: "圆圆",
姓名() {
return this.姓 + this.名;
},
age: 18
};
console.log( obj1.姓名());
// 高圆圆
let obj2 = {
姓: "高",
名: "圆圆",
get 姓名() {
return this.姓 + this.名;
},
age: 18
};
console.log( obj2.姓名);
// 高圆圆
- 对比代码,发现只是在姓名函数处添加了 get ,最后使用的时候就不再需要加 ()
- getter 就是这样用的,不加括号的函数
setter
let obj3 = {
姓: "高",
名: "圆圆",
get 姓名() {
return this.姓 + this.名;
},
set 姓名(xxx){
this.姓 = xxx[0]
this.名 = xxx.slice(1)
},
age: 18
};
obj3.姓名 = '刘诗诗'
console.log(`姓 ${obj3.姓},名 ${obj3.名}`)
// 姓 刘,名 诗诗
- setter 就是这样用的,用 = xxx 触发 set 函数
Object.defineProperty
用于给对象添加新属性,也可以给对象添加 getter/setter
let obj = {}
Object.defineProperty(obj, 'x', {value: 1})
Object.defineProperty(obj, 'y', {
get(){...}
set(value){...}
})
监听与代理
- 对 myData 对象的属性读写,全权由另一个对象 vm 负责
- 那么 vm 就是 myData 的代理 (类似房东租房通过中介)
- 比如 myData.n 不用,偏要用 vm.n 来操作 myData.n
代码案例:bold-heyrovsky-8jcm2 - CodeSandbox
vm = new Vue({data: myData}) 就做了和上面代码类似的事情。
- vm 成为了 myData 的代理
- 会对 mydata 的所有属性进行监控
目的: 无论直接修改 myData.n 还是修改 vm.n,vm 都会收到通知,然后调用触发重新渲染
Vue 对 methods 和 computed 也有类似处理。
Object.defineProperty 的问题
Object.defineProperty(obj, 'n', {...}
必须有 'n' 才可以监听&代理 obj.n
//无 data.n
new Vue({
data: {},
template: `
{{n}}
`
}).$mount("#app");
无 data.n 或 data.n 的值为 undefined ,此时 n 被引用,不会有显示,控制台会报警告
//有 data.obj,但无 data.obj.n
new Vue({
data: {
obj: {
a: 0 // obj.a 会被 Vue 监听 & 代理
}
},
template: `
{{obj.b}}
`,
methods: {
setB() {
this.obj.b = 1; //不显示1
}
}
}).$mount("#app");
data.obj 中开始并没有 b,后面运行 this.obj.b = 1 并不会让 1 出现在页面中,因为 vm 没有监听&代理 data.obj.b
解决办法:
- 在开始就定义好 data.obj.n = undefined
- 使用 Vue.set 或者 this.$set
一个特例:
//html
{{obj.a}}
{{obj.b}}
js
//js
var app = new Vue({
el: '#app',
data: {
obj: {
a: 'a',
}
},
})
app.obj.a = 'a2'
app.obj.b = 'b'
最终 span-a 中会显示 a2,span-b 中显示 b
这是因为视图更新是异步的,a1 变成 a2 时,Vue 监听到这个变化,并不会马上更新视图,而是创建一个视图更新任务到任务队列里。然后继续运行代码 app.obj.b = 'b'。视图更新时,Vue 会去做 diff,发现 a 和 b 都变了,于是去更新 span-a 和 span-b。
Vue.set 和 this.$set
作用:
- 新增key
- 自动创建代理和监听(如果没有创建过)
- 触发UI更新(不会立即更新)
this.$set(this.object,'m',100)
变异方法
在数组中,数组的长度一直增加,下标就是key,没办法提前声明所有下标,难道每次改数组都要Vue.set 或者 this.$set?
new Vue({
data: {
array: ["a", "b", "c"]
},
template: `
{{array}}
`,
methods: {
setD() {
this.array[3] = "d"; //不显示d
}
}
}).$mount("#app");
使用变异方法
push()、pop()、shift()、unshift()、splice()、sort()、reverse()
new Vue({
data: {
array: ["a", "b", "c"]
},
template: `
{{array}}
`,
methods: {
setD() {
this.array.push('d'); //修改后的push可完成我们想要的操作
}
}
}).$mount("#app");