背景
最近在做一些好玩的东西的时候遇到了一些麻烦。场景是这样的:我创建了一个ComponentTree这样的对象,数据结构定义大概是这样的。
interface ComponentTree {
id: number;
children: ComponentTree[];
}
而我需要监听这一颗树,在这个树任意数据改变时都要做出相应的操作,所以想到了使用Proxy。
Proxy介绍
Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。
通过 Proxy,你可以拦截并定义自定义的操作,以便在目标对象上进行这些操作之前执行一些自定义逻辑。
一般的用法是创建一个代理对象,传入目标对象和一个处理程序对象(handler),处理程序对象中定义了拦截器方法,如 get、set、apply 等。
代理对象可以用来实现属性验证、数据绑定、日志记录、虚拟属性等功能。
监听深层对象属性
/**通过这个监听类来监听ComponentTree */
export class WatcherComponentTree {
constructor(tree: ComponentTree, onChange: () => void) {
this.onChange = onChange;
this.proxyTree = this.observe(tree);
}
/**监听的树 */
public proxyTree: ComponentTree;
/**监听处理函数 */
public onChange: () => void;
/**
* 监听ComponentTree Proxy Reflect
* @param tree ComponentTree
* @returns Proxy<ComponentTree>
*/
public observe(tree: ComponentTree) {
// 深层遍历对象,给每个属性为对象的添加监听
return this.createProxy(tree);
}
/**
* 深度遍历创建proxy
* @param data 需要创建proxy的数据
* @returns Proxy<any> | any
*/
private createProxy(data: any) {
if (_.isObject(data)) {
if (_.isArray(data)) {
for (let i = 0; i < data.length; i++) {
data[i] = this.createProxy(data[i]);
}
} else {
Object.keys(data).forEach((keyItem) => {
(data as any)[keyItem] = this.createProxy((data as any)[keyItem]);
});
}
return new Proxy(data, {
get: (target: ComponentTree, key: string, receiver): any => {
return Reflect.get(target, key, receiver);
},
set: (target: ComponentTree, key: string, value) => {
Reflect.set(target, key, this.createProxy(value));
// 监听逻辑
this.onChange();
return true;
},
});
}
return data;
}
}
其实上面的逻辑总结起来很简单,在创建一个Proxy的时候递归地遍历对象所有属性,如果属性也是对象的话则同样也返回一个Proxy的代理对象,相当于递归地监听深层属性。当然不要忘记新加的属性,也就是在set的时候如果是对象也需要创建一个Proxy的代理对象。这样,我们就实现了深层监听一个对象