前言
本系列查阅顺序:
-
vue2 数据响应式原理——数据劫持(初始篇)
-
vue2 数据响应式原理——数据劫持(对象篇)
-
vue2 数据响应式原理——数据劫持(数组篇)
-
vue2 数据响应式原理——依赖收集和发布订阅
通过上一篇想必你已经对 Object.defineProperty()
有了一定的理解,这一篇我们就在前面的基础上探讨如何通过 Object.defineProperty()
来对对象,甚至是嵌套的对象进行数据劫持,以便我们能够侦听到对象的变化。
对 Object.defineProperty()
了解之后我们就可以对其进行封装,形成一个可以侦听到对象变化的函数:
数据劫持(对象篇)
defineReactive
function defineReactive(data, key, val) {
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
// 读取key时触发 getter
get: function() {
return val;
},
//更改key时触发 setter
set: function(newVal) {
if (val === newVal) {
return;
}
val = newVal;
},
});
}
复制代码
这里的 defineReactive
用来对 Object.defineProperty
进行封装。从函数名字可以看出,其作用是定义一个响应式数据。也就是在这个函数中进行变化追踪,封装后只需要传入 data
、 key
和 val
就行了。
封装后之后,每当从 data
的 key
中读取数据时, get
函数会被触发;每当 data
中的 key
改变时, set
函数会被触发
这里再看开头尤大大的那句话 在 getter 中收集依赖,在 setter 中触发依赖。
你应该就能明白我们之后需要在 defineReactive
的 get
函数中收集依赖,在 set
函数中触发依赖,也就是在访问对象的属性时收集依赖,在更新对象的属性时触发依赖,什么是依赖,我们之后再说。
这里演示一下 defineReactive
的用法:
function defineReactive(data, key, val) {
Object.defineProperty(data, key, {
//可枚举
enumerable: true,
//可以被配置,比如delete
configurable: true,
// 读取key时触发 getter
get: function() {
console.log(`你试图访问${data}的${key}属性,它的值为:${val}`);
return val;
},
//更改key时触发 setter
set: function(newVal) {
console.log(`你试图改变${data}的${key}属性,它的新值为:${newVal}`);
if (val === newVal) {
return;
}
val = newVal;
},
});
}
let obj = {
a: 6,
b: 9,
};
//将obj变成响应式数据
console.log(obj);
defineReactive(obj, "a", 6);
defineReactive(obj, "b", 9);
console.log(obj);
console.log("a", obj.a);
console.log("b", obj.b);
obj.a = "改变后的a";
console.log(obj.a);
复制代码