先给一个简单的实例,分析依赖收集和更新的过程
let obj = { name: "hefeng6500", age: 10, flag: true }
const state = reactive(obj);
effect(() => {
app.innerHTML = state.name + state.age
})
setTimeout(() => {
state.age = 20
}, 1000)
reactive 函数只会创建一个响应式对象,没有产生其他的什么副作用,仅在 state
对象在访问或者修改时才会触发 proxy
的 getter
和 setter
依赖收集
当 effect 函数在执行时
- Vue 底层会创建一个
ReactiveEffect
实例,创建后调用该实例的run
方法。run
方法实际就是传入effect
的回调函数。 - 在执行
run
方法前,将activeEffect = this
置为 this,也就是activeEffect
是当前创建的ReactiveEffect
实例。接着执行 run 方法 - 执行
run
方法时,会访问state.name
和state.age
,这就会触发两次 proxy 的 getter - 每触发一次 getter,就会进行一次依赖收集,也就是
track(target, key)
track
执行时会从targetMap
上获取当前taget
是否存在,未存在就targetMap.set(target, (depsMap = new Map()))
接着,在depsMap
上寻找key
是否存在不存在就设置depsMap.set(key, new Map())
。
最后 depsMap 保存格式如下:
- 执行
track
时,内部调用了trackEffect
,它将刚刚的activeEffect.deps
数组上添加了name
和age
这两个dep
,并且 这两个dep
上分别保存了 这个activeEffect
。形成了双向记忆。
触发更新
当执行 state.age = 20
时
- 触发 proxy 的 setter,进而触发
trigger
函数,找出 age 对应的 dep。调用triggerEffects(dep)
triggerEffects
拿到dep
后将该dep
上所有的effect
拿出来,调用它的scheduler
(其实就是run
方法,也就是 effect 传入的回调函数)