Vue 3.2.26 响应式核心:Effect 机制深度剖析
Vue 3 的响应式系统建立在 effect 这一核心抽象之上。本文通过源码视角,解析依赖收集与触发执行的完整链路。
Effect 实例的创建与运行
当调用 effect(fn) 时,内部会实例化一个响应式副作用对象:
const runner = new ReactiveEffect(fn)
// 立即执行以完成首次依赖收集
runner.run()
ReactiveEffect 的 run 方法实现了精密的栈管理机制:
run() {
// 避免嵌套执行时的重复入栈
if (!stack.includes(this)) {
try {
// 压栈并标记当前激活的 effect
stack.push((currentEffect = this))
openTracking()
// 位运算标记层级深度
trackBit = 1 << ++trackDepth
// 根据深度选择优化策略
if (trackDepth <= maxBits) {
markDeps(this) // 标记已有依赖
} else {
clearEffect(this) // 深度过大时全量清理
}
return this.fn() // 执行用户函数,触发依赖收集
} finally {
if (trackDepth <= maxBits) {
sweepDeps(this) // 清理失效依赖
}
trackBit = 1 << --trackDepth
closeTracking()
// 出栈并恢复上一个激活的 effect
stack.pop()
currentEffect = stack.length > 0
? stack[stack.length - 1]
: undefined
}
}
}
依赖收集:track 的实现
当响应式数据被读取时,track 函数建立数据与 effect 的映射关系:
export function track(target, type, key) {
// 仅在追踪状态下执行
if (!shouldTrack()) return
// 获取或创建 target -> depsMap 的映射
let keyToDeps = proxyMap.get(target)
if (!keyToDeps) {
proxyMap.set(target, (keyToDeps = new Map()))
}
// 获取或创建 key -> dep 的映射
let dep = keyToDeps.get(key)
if (!dep) {
keyToDeps.set(key, (dep = createDep()))
}
// 开发环境下携带调试信息
const debugInfo = __DEV__
? { effect: currentEffect, target, type, key }
: undefined
// 将当前 effect 加入依赖集合
addSubscriber(dep, debugInfo)
}
数据结构呈现三级嵌套:WeakMap<Target, Map<Key, Set<Effect>>>,确保垃圾回收不受阻碍。
依赖触发:trigger 的分发策略
数据变更时,trigger 根据操作类型精准定位待执行的 effect:
export function trigger(target, type, key, newVal, oldVal) {
const keyToDeps = proxyMap.get(target)
if (!keyToDeps) return // 从未被追踪,直接返回
const effectsToRun = []
// 操作类型分发
if (type === TriggerTypes.CLEAR) {
// 集合清空:触发所有依赖
effectsToRun.push(...keyToDeps.values())
}
else if (key === 'length' && isArray(target)) {
// 数组长度变化:收集索引相关的依赖
keyToDeps.forEach((dep, k) => {
if (k === 'length' || k >= newVal) {
effectsToRun.push(dep)
}
})
}
else {
// SET | ADD | DELETE 操作
if (key !== undefined) {
effectsToRun.push(keyToDeps.get(key))
}
// 迭代器依赖的特殊处理
switch (type) {
case TriggerTypes.ADD:
if (!isArray(target)) {
effectsToRun.push(keyToDeps.get(ITERATE_KEY))
if (isMap(target)) {
effectsToRun.push(keyToDeps.get(MAP_ITERATE_KEY))
}
} else if (isIntegerKey(key)) {
// 新增数组索引触发 length 更新
effectsToRun.push(keyToDeps.get('length'))
}
break
case TriggerTypes.DELETE:
if (!isArray(target)) {
effectsToRun.push(keyToDeps.get(ITERATE_KEY))
if (isMap(target)) {
effectsToRun.push(keyToDeps.get(MAP_ITERATE_KEY))
}
}
break
case TriggerTypes.SET:
if (isMap(target)) {
effectsToRun.push(keyToDeps.get(ITERATE_KEY))
}
break
}
}
// 去重并调度执行(实际源码包含 scheduler 逻辑)
const effects = new Set(effectsToRun.flat())
effects.forEach(effect => {
if (effect !== currentEffect) {
if (effect.scheduler) {
effect.scheduler()
} else {
effect.run()
}
}
})
}
关键设计要点
- 栈结构:通过
effectStack支持嵌套 effect 的正确关联 - 位运算优化:利用
trackOpBit和effectTrackDepth实现高效的依赖标记与清理 - 迭代器键:
ITERATE_KEY和MAP_KEY_ITERATE_KEY处理for...in、for...of等遍历场景 - 数组特化:区分索引操作与长度变更的依赖关系