Proxy 代理的是整个对象,而不是对象的某个属性,故而不需要通过遍历来逐个进行数据绑定。另外使用了 proxy ,name所有的操作对象都是 proxy 返回的实例对象,而不是原对象。这点与 defineProperty 是不同的,defineProperty 是对原对象进行操作。
使用 Object.defineProperty 存在的问题有:
- 一次只能对一个属性进行监听,需要遍历来处理监听所有属性。
- 在遇到一个对象的属性也是一个对象时,需要递归监听
- 对于对象的新增属性,需要手动监听
- 对于数组通过 push、unshift 方法增加的元素,也无法监听
/**
* 新增属性,需要手动设置 getter 和 setter,且一次只能对一个属性进行监听
*/
let person = {
age: 36,
};
const createReactive = (obj, key, val) => {
Object.defineProperty(obj, key, {
get() {
console.log(`Getting value of ${key}: ${val}`);
return val;
},
set(newVal) {
console.log(`Setting value of ${key} to ${newVal}`);
if (newVal !== val) {
val = newVal;
}
},
enumerable: true,
configurable: true,
});
};
createReactive(person, 'name', 'Alice');
let person = {
name: "Older Join",
age: 57,
}
const createReactive = obj => {
Object.keys(obj).forEach(key => {
reactiveImplement(obj, key, obj[key])
})
}
const reactiveImplement = (obj,key,val) => {
Object.defineProperty(obj, key, {
get() {
console.log(`初始化 属性名${key}`);
return val;
},
set(newVal) {
console.log(`设置属性${key}, 值为${newVal}`);
val = newVal;
}
})
}
createReactive(person);
person.age; // 初始化 属性名age
person.name = 'Tom'; // 设置属性name, 值为Tom
let person = {
name: 'Older Join',
age: 57,
children: [
{
name: 'Join',
age: 37,
children: [
{
name: 'little Join',
age: 3,
},
],
},
],
};
const createReactive = obj => {
if (typeof obj !== 'object' || obj === null) return;
Object.keys(obj).forEach(key => {
reactiveImplement(obj, key, obj[key]);
});
};
const reactiveImplement = (obj, key, val) => {
if (typeof val === 'object' && val !== null) {
createReactive(val); // 递归地处理嵌套对象
}
Object.defineProperty(obj, key, {
get() {
console.log(`初始化 属性名${key}`);
return val;
},
set(newVal) {
if (typeof newVal === 'object' && newVal !== null) {
createReactive(newVal); // 递归地处理新的对象值
}
console.log(`设置属性${key}, 值为${newVal}`);
val = newVal;
},
});
};
createReactive(person);
// 测试嵌套对象的响应式
console.log(person.children[0].name); // 初始化 属性名name
person.children[0].name = 'John'; // 设置属性name, 值为John
console.log(person.children[0].name); // 初始化 属性名name
person.children[0].children[0].age = 4; // 设置属性age, 值为4
console.log(person.children[0].children[0].age); // 初始化 属性名age
let person = {
name: 'John',
age: 27,
};
const createReactive = target => {
const handler = {
get(target, key, receiver) {
console.log('get', key);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log('set', key, value);
return Reflect.set(target, key, value, receiver);
},
};
return new Proxy(target, handler);
};
const userInfo = createReactive(person);
userInfo.name = 'Tom'; // set name Tom
userInfo.age; // get age
下面的例子中,我们打印了嵌套对象的深层属性 age,但是 get 拦截的属性是 children。
另外我们尝试 对 深层属性name赋值,结果打印的也是 get children,所以 set 拦截也没进入。
const createReactive = target => {
const handler = {
get(target, key, receiver) {
console.log('get', key);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log('set', key, value);
return Reflect.set(target, key, value, receiver);
},
};
return new Proxy(target, handler);
};
let person2 = {
name: 'Older Join',
age: 57,
children: [
{
name: 'Join',
age: 37,
children: [
{
name: 'little Join',
age: 3,
},
],
},
],
};
const depUserInfo = createReactive(person2);
depUserInfo.children[0].age; // get children
const createReactive = target => {
const handler = {
get(target, key, receiver) {
console.log('get', key);
// 处理 深层嵌套
if (typeof target[key] === 'object') {
return createReactive(target[key]);
}
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log('set', key, value);
return Reflect.set(target, key, value, receiver);
},
};
return new Proxy(target, handler);
};
let person2 = {
name: 'Older Join',
age: 57,
children: [
{
name: 'Join',
age: 37,
children: [
{
name: 'little Join',
age: 3,
},
],
},
],
};
const depUserInfo = createReactive(person2);
depUserInfo.children[0].age;
depUserInfo.children[0].name = 'lucky Join';
Reactive 的懒响应性 指的是: Reactive 最初只会为复杂数据类型执行第一层的响应性。如果存在多层的复杂数据类型嵌套时,则会在使用到该子集时,才会再次为该子集包装 proxy 代理。参考上面的 depUserInfo.children[0].name = 'lucky Join';
另外,帖一下 vue3 中的相关代码:
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
const res = Reflect.get(target, key, receiver)
if (isObject(res)) {
// Convert returned value into a proxy as well. we do the isObject check
// here to avoid invalid value warning. Also need to lazy access readonly
// and reactive here to avoid circular dependency.
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}