Vue3的Effect是其响应式系统的核心,负责依赖追踪和自动响应,它通过ReactiveEffect类封装副作用逻辑,实现依赖收集和触发更新,本文介绍vue 3 effect作用与原理解析,感兴趣的朋友一起看看吧 特性 Vue 2 (Watcher) Vue 3 (Effect) 依赖追踪通过遍历数据触发
目录
一、核心作用1.依赖追踪(Dependency Tracking)2.自动响应(Automatic Re-run)3.支撑高级 API二、实现原理1.核心类:ReactiveEffect2.依赖收集流程(Track)3.触发更新(Trigger)4.调度器(Scheduler)三、关键优化设计1.嵌套 Effect 栈2.Cleanup 机制3.Lazy 执行四、与 Vue 各组件的关联1.组件渲染2.Computed 实现3.Watch API五、与 Vue 2 的对比六、源码流程图解总结Vue 3 的Effect(副作用)是整个响应式系统的核心机制,负责管理依赖追踪和响应式触发。理解其作用和原理对掌握 Vue 的底层机制至关重要。
一、核心作用
1.依赖追踪(Dependency Tracking)
自动跟踪响应式数据在副作用函数中的使用。示例代码:
import { reactive, effect } from 'vue'const obj = reactive({ count: 0 })effect(() => { console.log(`count is: ${obj.count}`)})当首次执行
effect
时,函数() => console.log(...)
会被运行。触发obj.count
的get
操作,触发依赖收集(将当前effect
关联到obj.count
)。2.自动响应(Automatic Re-run)
当响应式数据的依赖变化时,自动重新执行副作用函数:
obj.count++ // 触发依赖更新,控制台打印 "count is: 1"
3.支撑高级 API
computed
、watch
、组件渲染函数等底层都依赖于effect
实现。二、实现原理
1.核心类:ReactiveEffect
Vue 3 用ReactiveEffect
类封装副作用逻辑,简化后的源码结构如下:
class ReactiveEffect<T = any> { // 当前 effect 的所有依赖项(其他响应式对象) deps: Dep[] = [] // 构造函数参数 constructor( public fn: () => T, // 副作用函数 public scheduler?: () => void // 调度函数(控制重新执行方式) ) {} // 运行副作用(触发依赖收集) run() { activeEffect = this // 标记当前正在运行的 effect try { return this.fn() } finally { activeEffect = undefined } } // 停止侦听 stop() { /* 从所有依赖中移除自身 */ }}
2.依赖收集流程(Track)
数据结构:
type Dep = Set<ReactiveEffect> // 依赖集合type TargetMap = WeakMap<Object, Map<string, Dep>> // 全局依赖存储触发时机:响应式数据的
get
操作触发时。流程:根据响应式对象 (target
) 和键 (key
) 找到存入targetMap
的依赖集合 (dep
)。
将当前活跃的activeEffect
添加到dep
中。
同时将dep
加入activeEffect.deps
(反向记录,用于 cleanup)。
3.触发更新(Trigger)
触发时机:响应式数据的set
操作时。流程:根据target
和key
从targetMap
获取对应的dep
集合。
遍历dep
中所有effect
:
scheduler
(如computed
),执行调度器(优化性能)。否则直接执行effect.run()
。4.调度器(Scheduler)
允许控制effect
如何重新执行:
effect(() => { console.log(obj.count)}, { scheduler(effect) { // 如将 effect 推入微任务队列中异步执行 queueMicrotask(effect.run) }})应用场景:
watch
的异步批处理更新。computed
的值懒更新。三、关键优化设计
1.嵌套 Effect 栈
用栈结构effectStack
跟踪嵌套的 effect:
function run() { if (!effectStack.includes(this)) { try { effectStack.push((activeEffect = this)) return this.fn() } finally { effectStack.pop() activeEffect = effectStack[effectStack.length - 1] } }}解决问题:组件嵌套时的依赖关系混乱。
2.Cleanup 机制
每次 effect 执行前清理旧依赖:
function run() { cleanup(this) // 清理之前收集的旧依赖 // ...然后重新收集新依赖}解决问题:动态分支逻辑导致的无效依赖(如
v-if
切换导致的条件依赖)。3.Lazy 执行
可配置不立即执行 effect:
const runner = effect(fn, { lazy: true })runner() // 手动执行应用场景:
computed
属性初始化时延迟计算。四、与 Vue 各组件的关联
1.组件渲染
组件render
函数被包裹在effect
中:
function setupRenderEffect(instance) { effect(() => { const subTree = instance.render.call(instance.proxy) patch(instance.subTree, subTree) instance.subTree = subTree }, { scheduler: queueJob }) // 异步更新队列}
2.Computed 实现
computed
通过effect
+ 调度器实现懒更新:
const computedRef = new ComputedRefImpl( getter, () => { // 调度器 if (!this._dirty) { this._dirty = true trigger(this, 'set', 'value') } })
3.Watch API
watch
基于effect
的调度器实现异步回调:
function watch(source, cb, { flush } = {}) { let scheduler if (flush === 'sync') { scheduler = cb } else { // 'post' 或其他默认情况 scheduler = () => queuePostFlushCb(cb) } effect(() => traverse(source), { scheduler })}
五、与 Vue 2 的对比
getter
通过 Proxy/Reflect 自动追踪更新粒度依赖组件级检查基于精确依赖的靶向更新性能优化需手写pureComputed
等内置自动的依赖清理和调度机制内存管理易产生内存泄漏(旧 Dep 引用问题)通过 WeakMap 自动释放无用依赖六、源码流程图解
+---------------------+| Reactive Object |+----------+----------+ │ 访问属性时 ▼+---------------------+| 触发 get 代理 +----→ track(target, key)+---------------------+ │ ▲ ▼ 存储依赖关系 │ +---------------------+ +----------+ targetMap | | (WeakMap结构) | +---------+-----------+ │ ▼ +---------------------+ | depsMap (Map) | | (key → Dep Set) | +---------+-----------+ │ ▼ +---------------------+ | dep (Set) | | (存储所有关联的 effect)| +---------------------+
总结
Vue 3 的effect
通过以下机制成为响应式系统的核心:
WeakMap
自动管理依赖。框架级优化:支持组件渲染、计算属性、watch 等核心功能。