let product = reactive({ price: 5, quantity: 2 }); let total = 0; let salePrice = 0; let targetMap = newWeakMap(); // 设置当前激活的响应effect函数 let activeEffect = null;
watcherEffect(() => { total = product.price * product.quantity; });
// 收集依赖 functiontrack(target, key) { // 只有当前是激活的effect才会收集依赖,也就是effect函数中执行的才会收集依赖 if (activeEffect) { let depsMap = targetMap.get(target); if (!depsMap) { targetMap.set(target, (depsMap = newMap())); } let dep = depsMap.get(key); if (!dep) { depsMap.set(key, (dep = newSet())); } dep.add(activeEffect); } }
// 更新 functiontrigger(target, key) { // 通过当前对象获取到对应的map(当前对象的属性和vlaue) let depsMap = targetMap.get(target); if (!depsMap) return; // 通过对象的属性,获取到当前属性依赖的effect函数(set集合) let dep = depsMap.get(key); if (dep) { dep.forEach((effect) =>effect()); } }
functionreactive(target) { let hanlder = { get(target, key, receiver) { let res = Reflect.get(target, key, receiver); // track我们将收集依赖映射(当前对象weakmap -> 属性map -> effect(set集合)) track(target, key); return res; }, set(target, key, value, receiver) { // 获取到之前的数值 let oldVal = target[key]; let result = Reflect.set(target, key, value, receiver); if (result && oldVal !== value) { // 更新 trigger(target, key); } return result; }, }; returnnewProxy(target, hanlder); }
3. 总结
通过 Proxy 代理中的 get 和 set 拦截对象属性,当在 get 的时候说明在读取属性,需要我们进行对象-属性-依赖函数之间的关系映射,并且要将当前执行的函数添加到该属性对应的 set 集合中。要想知道当前执行的函数是谁,我们通过将要执行的函数交给一个watcherEffect函数,一个巧妙的全局变量,存储当前正在执行的函数,然后将该函数添加到 set 集合中。然后在给响应式对象赋值的时候,会进入到 proxy 的 set 拦截函数,这个时候就会替我们执行trigger函数,执行我们在track时候收集到该属性对应的依赖函数。实现了一个简单的响应式。