// -------------创建响应式-------------
// set 数组,用作保存所有的运算函数
let deps = new Set();
// 保存运算函数
function track() {
deps.add(effect);
}
// 触发器,执行所有的运算函数
function trigger() {
deps.forEach((effect) => effect());
}
// -------------创建数据源-------------
// 声明商品对象,为数据源
let product = {
price: 10,
quantity: 2
};
// 声明总价格
let total = 0;
// 运算总价格的匿名函数
let effect = () => {
total = product.price * product.quantity;
};
// -------------执行响应式-------------
// 保存运算函数
track();
// 运算 总价格
effect();
console.log(总价格:${total}
); // 总价格:20
// 修改数据源
product.quantity = 5;
// 数据源被修改,执行触发器,重新运算所有的 total
trigger();
console.log(总价格:${total}
); // 总价格:50
你对你的 创造 非常骄傲,并且开始把它推荐给周边的朋友进行使用。但是很快,就有人提出了问题:我 希望把响应式作用到对象的具体属性中 ,而不是 一个属性改变,全部计算重新执行。
响应性绑定对象,导致 一个属性改变,全部计算重新执行。所以你希望把响应式作用到对象的具体属性中,只 重新运算该属性相关的内容
为了实现这个功能,你需要借助 Map 对象。
Map
以 key:val
的形式存储数据,你希望以 属性为 key
,以该属性相关的运算方法集合为 val
。以此你构建了一个 depsMap
对象,用来达到你的目的:
// -------------创建响应式-------------
// Key:Val 结构的集合
let depsMap = new Map();
// 为每个属性单独保存运算函数,从而让每个属性具备自己独立的响应式
function track(key, eff) {
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
dep.add(eff)
}
// 触发器,执行指定属性的运算函数
function trigger(key) {
// 获取指定函数的 dep 数组
const dep = depsMap.get(key);
// 遍历 dep,执行指定函数的运算函数
if (dep) {
dep.forEach((eff) => eff());
}
}
// -------------创建数据源-------------
// 声明商品对象,为数据源
let product = {
price: 10,
quantity: 2
};
// 声明总价格
let total = 0;
// 运算总价格的匿名函数
let effect = () => {
total = product.price * product.quantity;
};
// -------------执行响应式-------------
// 保存运算函数
track(‘quantity’, effect);
// 运算 总价格
effect();
console.log(总价格:${total}
); // 总价格:20
// 修改数据源
product.quantity = 5;
// quantity 被修改,仅仅触发 quantity 的响应式
trigger(‘quantity’);
console.log(总价格:${total}
); // 总价格:50
你的客户总是非常挑剔的,很快他们抛出了新的问题:我的程序不可能只有一个对象!你需要让所有的对象都具备响应式!
你的响应式需要覆盖程序中的所有对象,否则你的代码将毫无意义!
为了达到这个目的,你需要将 对象、属性、运算方法 进行分别的缓存,现有的 depsMap
已经没有办法满足你了。你需要更加强大的 Map
,让 每个对象 都有一个 Map
。它就是 WeakMap。
WeakMap 对象是一组键/值对的集合。其键必须是对象,而值可以是任意的。
借助 WeakMap
你让每个对象都拥有了一个 depsMap
:
// -------------创建响应式-------------
// weakMap:key 必须为对象,val 可以为任意值
const targetMap = new WeakMap()
// 为不同对象的每个属性单独保存运算函数,从而让不同对象的每个属性具备自己独立的响应式
function track(target, key, eff) {
// 获取对象所对应的 depsMap
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
// 获取 depsMap 对应的属性
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
// 保存不同对象,不同属性的 运算函数
dep.add(eff)
}
// 触发器,执行指定对象的指定属性的运算函数
function trigger(target, key) {
// 获取对象所对应的 depsMap
let depsMap = targetMap.get(target)
if (!depsMap) {
return
}
// 获取指定函数的 dep 数组
const dep = depsMap.get(key);
// 遍历 dep,执行指定函数的运算函数
if (dep) {
dep.forEach((eff) => eff());
}
}
// -------------创建数据源-------------
// 声明商品对象,为数据源
let product = {
price: 10,
quantity: 2
};
// 声明总价格
let total = 0;
// 运算总价格的匿名函数
let effect = () => {
total = product.price * product.quantity;
};
// -------------执行响应式-------------
// 保存运算函数
track(product, ‘quantity’, effect);
// 运算 总价格
effect();
console.log(总价格:${total}
); // 总价格:20
// 修改数据源
product.quantity = 5;
// quantity 被修改,仅仅触发 quantity 的响应式
trigger(product, ‘quantity’);
console.log(总价格:${total}
); // 总价格:50
每次数据改变,我都需要重新执行 trigger
, 这样太麻烦了!万一我忘了怎么办? 。客户总是会提出一些 改(wu)进(li)
的要求,没办法,谁让人家是客户呢?
每次数据改变,我都需要重新执行
trigger
,你的客户发出了这样的抱怨。
如果想要达到这样的目的,那么你需要了解 “数据的行为” , 即:你需要知道,数据在什么时候被赋值,在什么时候被输出。
此时你需要借助两个新的对象:
借助 Proxy + Reflect
你成功实现了对数据的监听:
// -------------创建响应式-------------
// weakMap:key 必须为对象,val 可以为任意值
const targetMap = new WeakMap()
// 为不同对象的每个属性单独保存运算函数,从而让不同对象的每个属性具备自己独立的响应式
function track(target, key, eff) {
// 获取对象所对应的 depsMap
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
// 获取 depsMap 对应的属性
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
// 保存不同对象,不同属性的 运算函数
dep.add(eff)
}
// 触发器,执行指定对象的指定属性的运算函数
function trigger(target, key) {
// 获取对象所对应的 depsMap
let depsMap = targetMap.get(target)
if (!depsMap) {
return
}
// 获取指定函数的 dep 数组
const dep = depsMap.get(key);
// 遍历 dep,执行指定函数的运算函数
if (dep) {
dep.forEach((eff) => eff());
}
}
// 使用 proxy 代理数据源,以达到监听的目的
function reactive(target) {
const handlers = {
get(target, key, receiver) {
track(target, key, effect)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
let oldValue = target[key]
let result = Reflect.set(target, key, value, receiver)
if (result && oldValue != value) {
trigger(target, key)
}
return result
},
}
return new Proxy(target, handlers)
}
// -------------创建数据源-------------
// 声明商品对象,为数据源
let product = reactive({ price: 10, quantity: 2 })
// 声明总价格
let total = 0;
// 运算总价格的匿名函数
let effect = () => {
total = product.price * product.quantity;
};
// -------------执行响应式-------------
effect()
console.log(总价格:${total}
); // 总价格:20
// 修改数据源
product.quantity = 5;
console.log(总价格:${total}
); // 总价格:50
你心满意足,觉得你的代码无懈可击。突然耳边响起客户 赏(bu)心(he)悦(shi)目(yi)
的声音:你不觉得每次执行 effect
很反人类吗?
自动化!自动化!所有的操作都应该自动运行!
为了可以让运算自动运行,你专门设计了一个 effect
函数,它可以 接收运算函数,并自动执行
// -------------创建响应式-------------
// weakMap:key 必须为对象,val 可以为任意值
const targetMap = new WeakMap()
// 运算函数的对象
let activeEffect = null;
// 为不同对象的每个属性单独保存运算函数,从而让不同对象的每个属性具备自己独立的响应式
function track(target, key) {
if (activeEffect) {
// 获取对象所对应的 depsMap
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
// 获取 depsMap 对应的属性
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
// 保存不同对象,不同属性的 运算函数
dep.add(activeEffect)
}
}
// 触发器,执行指定对象的指定属性的运算函数
function trigger(target, key) {
// 获取对象所对应的 depsMap
let depsMap = targetMap.get(target)
if (!depsMap) {
return
}
// 获取指定函数的 dep 数组
const dep = depsMap.get(key);
// 遍历 dep,执行指定函数的运算函数
if (dep) {
dep.forEach((eff) => eff());
}
}
// 使用 proxy 代理数据源,以达到监听的目的
function reactive(target) {
const handlers = {
get(target, key, receiver) {
track(target, key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
let oldValue = target[key]
let result = Reflect.set(target, key, value, receiver)
if (result && oldValue != value) {
trigger(target, key)
}
return result
},
}
return new Proxy(target, handlers)
}
// 接收运算函数,执行运算函数
function effect(eff) {
activeEffect = eff;
activeEffect();
activeEffect = null;
}
// -------------创建数据源-------------
// 声明商品对象,为数据源
let product = reactive({ price: 10, quantity: 2 })
// 声明总价格
let total = 0;
// 通过 effect 运算总价格
effect(() => {
total = product.price * product.quantity;
})
// -------------执行响应式-------------