let price = 5; let quantity = 2; let total = price * quantity;
console.log(`total is ${total}`); // total is 10
// 当我们价格发生改变的时候 price = 20;
// 再次查看total console.log(`total is ${total}`); // total is 10
很明显,我们的 total 总量并没有随着 price 价格改变而进行改变。原因很简单,我们的 javascript 代码是同步的,代码自上而下执行,当价格发生改变的时候,并不会影响到已经执行过的let total = price * quantity结果。
那么 vue 是如何实现响应式的呢?接下来我们一起,一步步的深入探索。
3. 响应式
3.1 初识 track、effect、trigger
假如说我们用某一个存储方式,将let total = price * quantity(effect)存储起来。在我们价格发生变化的时候,我们在执行一次存储起来的代码。这样我们的 total 就会随着价格的改变而发生变化了。
在这里我们用set来存储 effect,用 set 的好处就是:他不允许我们重复添加 effect。
functiontrack(target, key) { // 当前响应式对象是否建立映射关系 let depsMap = targetMap(target); if (!depsMap) { targetMap.set(target, (depsMap = newMap())); }
// 当前对象的属性,是否建立映射关系 let dep = depsMap.get(key);
if (!dep) { depsMap.set(key, (dep = newSet())); }
// 添加依赖 dep.add(effect); }
functiontrigger(target, key) { let depsMap = targetMap.get(target); // 检查当前响应式对象是否存在依赖,不存在则直接退出 if (!depsMap) return; let dep = depsMap.get(key); // 该对象的属性存在依赖函数,循环运行effect if (dep) { dep.forEach((effect) =>effect()); } }
let product = { price: 5, quantity: 2 }; let total = 0; let user = { name: "John", age: 30 }; leteffect = () => { total = product.price * product.quantity; };
// weakmap的key可以是对象 let targetMap = newWeakMap(); functiontrack(target, key) { let depsMap = targetMap.get(target); if (!depsMap) { // 映射当前对象和对象属性的关系 targetMap.set(target, (depsMap = newMap())); } // 查看当前对象的属性依赖函数effect是否存在
let dep = depsMap.get(key); if (!dep) { depsMap.set(key, (dep = newSet())); } // 添加当前对象下的属性依赖的effect函数 dep.add(effect); }
functiontrigger(target, key) { let depsMap = targetMap.get(target); if (!depsMap) return; let dep = depsMap.get(key); if (dep) { dep.forEach((effect) =>effect()); } }