手写 Vue 源码 === 依赖清理机制详解
目录
引言
响应式系统基础回顾
依赖清理的必要性
ReactiveEffect 类的设计
依赖清理的三个关键函数
1. preCleanEffect:执行前的准备
2. trackEffects:依赖收集与 diff 算法
3. postCleanEffect:执行后的清理
4. cleanDepEffect:清理依赖
实际案例分析
依赖清理算法的优化
总结
引言
Vue 的响应式系统是其核心特性之一,而依赖清理机制则是确保响应式系统高效运行的关键部分。本文将深入探讨 Vue 响应式系统中的依赖清理机制,特别是 preCleanEffect、 postCleanEffect 以及 trackEffects 中的 diff 算法,并通过实际案例分析其工作原理。
响应式系统基础回顾
在深入依赖清理之前,让我们先简单回顾一下 Vue 响应式系统的基本工作原理:
- 当我们使用 effect 函数包裹一个回调函数时,这个回调函数会立即执行
- 执行过程中,会访问响应式对象的属性,触发 getter
- 在 getter 中,会通过
track
函数收集当前正在执行的 effect 作为依赖 - 当响应式对象的属性发生变化时,会通过
trigger
函数触发收集的依赖,重新执行 effect
export function effect(fn, options: any = {}) {// 创建一个 effect 只要依赖的属性变化,就会重新执行const _effect = new ReactiveEffect(fn, () => {_effect.run();});// 执行_effect.run();
}
依赖清理的必要性
为什么需要依赖清理?考虑以下场景:
effect(() => {if (condition) {console.log(obj.a)} else {console.log(obj.b)}
})
当 condition
为 true
时,effect 依赖 obj.a
;当 condition
为 false
时,effect 依赖 obj.b
。
如果没有依赖清理机制,当 condition
从 true
变为 false
后,effect 仍然会保持对 obj.a
的依赖。这意味着当 obj.a
变化时,effect 会重新执行,但实际上 obj.a
已经不再被使用了。
ReactiveEffect 类的设计
首先,让我们看一下 ReactiveEffect 类的设计,它是依赖清理机制的核心:
class ReactiveEffect {_trackId = 0; // 当前的 effect 执行了几次deps = []; // 当前的 effect 依赖了哪些属性_depsLength = 0; // 当前的 effect 依赖的属性有多少个public active = true; //默认是响应式的constructor(public fn, public scheduler) {}run() {// 如果当前状态是停止的,执行后,啥都不做if (!this.active) {return this.fn();}let lastEffect = activeEffect;try {activeEffect = this; // 当前的 effect 「依赖收集」// 每次执行前需要将上一次的依赖清空 effect.depspreCleanEffect(this);