目录
[嵌套 effect 的处理](#嵌套 effect 的处理)
Vue3 的响应式系统核心在于跟踪依赖并在数据变化时触发更新。effect.ts
文件实现了这一机制的核心部分,下面我们来梳理其中的关键思路。
核心概念
// 创建一个响应式对象 effect
export function effect(fn, options: any = {}) {
// 创建一个 effect 只要依赖的属性变化,就会重新执行
const _effect = new ReactiveEffect(fn, () => {
_effect.run();
});
// 执行
_effect.run();
}
export let activeEffect; // 当前的 effect
响应式效果的实现
ReactiveEffect
类是整个响应式系统的核心,它负责:
-
存储用户传入的回调函数
-
提供执行机制
-
处理嵌套 effect 的情况
class ReactiveEffect {
/**
* fn 是用户传入的回调函数「如果fn中依赖了响应式数据,当数据变化后,会重新调用scheduler -->run」
* scheduler 是调度器
*/
public active = true; //默认是响应式的
constructor(public fn, public scheduler) {}
run() {
// 如果当前状态是停止的,执行后,啥都不做
if (!this.active) {
return this.fn();
}/** * effect(() => { console.log(state.name); effect(() => { console.log(state.name); }) console.log(state.age); }) 执行前,保存当前的 activeEffect 可以理解为 「栈」执行完后,恢复上一次的 activeEffect */ let lastEffect = activeEffect; try { activeEffect = this; // 当前的 effect 「依赖收集」 return this.fn(); //依赖收集 「state.name ,state.age」 } finally { activeEffect = lastEffect; // 执行完毕后 恢复上一次的 activeEffect }
}
}
activeEffect
是一个全局变量,它的核心作用是:标记当前正在执行的 effect。这是依赖收集的关键环节,让系统知道"当前访问的属性应该与哪个 effect 建立联系"
依赖收集的具体流程
让我们通过一个具体的例子来理解这个过程:
const state = reactive({ name: 'zhang', age: 18 });
effect(() => {
console.log(state.name); // 访问 name 属性
});
当这段代码执行时,发生了以下步骤:
-
调用 effect 函数,创建一个 ReactiveEffect 实例
-
调用 _effect.run()
-
在 run 方法中,将 activeEffect 设置为当前 effect 实例
-
执行用户传入的回调函数 fn()
-
在回调函数中访问 state.name
-
访问属性触发 Proxy 的 get 捕获器
-
get 捕获器调用 track 函数
get(target: any, key: any, receiver: any) {
// ...
track(target, key);
return Reflect.get(target, key, receiver);
}
track 函数检查 activeEffect 是否存在,如果存在,就建立依赖关系
export function track(target, key) {
if (activeEffect) {
console.log(activeEffect, key);
// 这里应该建立 target[key] 和 activeEffect 的映射关系
}
}
为什么使用全局变量?
使用全局变量activeEffect
的好处是:
- 简化 API 设计,用户不需要手动指定依赖关系
- 自动追踪运行时的依赖,只收集真正访问的属性
- 支持动态依赖收集,依赖可以根据条件变化
嵌套 effect 的处理
Vue3 巧妙地处理了嵌套 effect 的情况,通过维护当前活跃的 effect 和保存上一个 effect 的方式:
effect(() => {
console.log(state.name); // 外层 effect 依赖 state.name
effect(() => {
console.log(state.name); // 内层 effect 依赖 state.name
})
console.log(state.age); // 外层 effect 还依赖 state.age
})
当处理嵌套 effect 时,代码会:
- 保存当前的 activeEffect 到 lastEffect 变量
- 将 activeEffect 设置为当前执行的 effect
- 执行完毕后,恢复 activeEffect 为之前保存的值
- 这就像一个栈结构,确保每个属性访问都能正确关联到当前正在执行的 effect。
总结
Vue3 响应式系统的核心在于:
- 通过 effect 函数创建响应式效
- 果使用
ReactiveEffect
类管理回调函数和执行逻辑 - 通过 activeEffect 全局变量跟踪当前正在执行的 effect
- 在属性访问时进行依赖收集
- 在属性设置时触发相关 effect 重新执行
Vue3 响应式系统通过activeEffect
全局变量巧妙地解决了"谁在使用这个属性"的问题,实现了自动依赖收集。当访问响应式对象的属性时,系统知道是哪个 effect 正在运行,从而建立属性与 effect 之间的映射关系,为后续的响应式更新奠定基础。