前置知识:
精简后的处理
- 只考虑普通对象和
reactive() - 不考虑各种边界条件和判断,详细请看源码
数据结构
flowchart TD
rawObject ==>|"reactive(rawObject)"| reactiveObject
dep --> effect
deps --> dep
subgraph effect
deps["deps: dep[]"]
end
subgraph targetMap
reactiveObject -.-> depsMap
subgraph reactiveObject
get --> track
set --> trigger
end
subgraph depsMap
key -.-> dep["dep: effect Set"]
end
end
reactive
- 拦截
get执行track--> 依赖收集 - 拦截
set执行trigger--> 执行【副作用函数】
function reactive(rawObject) {
return new Proxy(rawObject, {
get(target, key) {
const res = Reflect.get(target, key)
// ⭐️ 依赖收集 ⭐️
track(target, key)
// 获取的值是对象,则返回前要做响应式处理
return typeof res === 'object' ? reactive(res) : res
},
set(target, key, value) {
const res = Reflect.set(target, key, value)
// ⭐️ 触发副作用 ⭐️
trigger(target, key)
return res
},
})
}
一些全局变量
// 当前正在执行的 effect 函数
let activeEffect
// 保存所有【响应式对象】和【其所有属性值对应的副作用】的映射
const targetMap = new Map()
createReactiveEffect
将一个【普通函数】封装成一个【副作用函数】
可以简单的认为:
- 【副作用函数】就是组件实例的【更新函数】
- 【更新函数】就是由组件的【渲染函数】经过副作用包装得到:
instance.update = createReactiveEffect(instance.render)
/**
* @param fn 一个函数,函数体里面使用到了【响应式对象】
*/
function createReactiveEffect(fn) {
const effect = () => {
// 清除依赖
effect.deps.forEach((dep) => {
dep.delete(effect)
})
try {
// 记录当前 effect,track 要用
activeEffect = effect
// ⭐️ 执行 fn() 的时候,会访问到其中的【响应式对象】,也就触发 get,最终触发了 track ⭐️
return fn()
} finally { // 即使前面 return,最后也必定执行
activeEffect = undefined
}
}
// 用来保存依赖 dep,作用就是前面的清除依赖
effect.deps = []
return effect
}
track
依赖收集,双向绑定
function track(target, key) {
// targetMap: target --> depsMap
let depsMap = targetMap.get(target)
if (!depsMap) { // 初始化
depsMap = new Map()
targetMap.set(target, depsMap)
}
// depsMap: key --> dep
let dep = depsMap.get(key)
if (!dep) { // 初始化
dep = new Set()
depsMap.set(key, dep)
}
// 双向绑定
if (!dep.has(activeEffect)) {
dep.add(activeEffect)
activeEffect.deps.push(dep)
}
}
trigger
执行对应的【副作用函数】
function trigger(target, key) {
const depsMap = targetMap.get(target)
const dep = depsMap.get(key)
for (const effect of dep) {
effect()
}
}