vue3源码
一、vue3 computed和watch源码
一、computed 的实现原理
1. 核心思想
-
惰性求值:只有在真正被访问时才会计算值,且依赖未变化时会复用缓存值。
-
依赖追踪:内部通过
effect跟踪依赖的响应式变量,当依赖变化时标记缓存失效。 -
触发更新:当缓存失效时,触发依赖它的副作用(如组件渲染)重新执行。
2. 源码结构
computed 的实现在 packages/reactivity/src/computed.ts 中,核心类是 ComputedRefImpl:
class ComputedRefImpl<T> {
private _value!: T
private _dirty = true // 标记是否需要重新计算
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true
constructor(
getter: () => T,
private readonly _setter: (v: any) => void
) {
// 创建一个 effect,用于依赖收集
this.effect = new ReactiveEffect(getter, () => {
if (!this._dirty) {
this._dirty = true
trigger(toRaw(this), TriggerOpTypes.SET, 'value') // 触发依赖更新
}
})
}
get value() {
// 访问时收集依赖
track(toRaw(this), TrackOpTypes.GET, 'value')
if (this._dirty) {
this._value = this.effect.run() // 重新计算值
this._dirty = false
}
return this._value
}
set value(newValue: any) {
this._setter(newValue) // 调用用户定义的 setter
}
}
3. 关键流程
-
初始化:创建
ReactiveEffect,传入getter和一个调度器函数(标记_dirty并触发更新)。 -
取值(get value):
-
通过
track收集当前正在运行的effect(如组件渲染的effect)。 -
如果
_dirty为true,运行getter计算新值并缓存。
-
-
依赖变化:
-
当
getter中的依赖变化时,触发调度器函数,标记_dirty = true。 -
通过
trigger通知所有依赖此computed的副作用重新执行。
-
-
赋值(set value):调用用户定义的
setter(通常用于可写计算属性)。
二、watch 的实现原理
1. 核心思想
-
监听变化:基于
effect监听一个或多个响应式数据源。 -
异步回调:默认将回调推入微任务队列,避免同步重复调用。
-
新旧值对比:通过
effect.run()获取新值,并保存旧值供回调使用。
2. 源码结构
watch 的实现在 packages/runtime-core/src/apiWatch.ts,核心函数是 doWatch:
function doWatch(
source: WatchSource,
cb: WatchCallback | null,
{ immediate, deep, flush }: WatchOptions = EMPTY_OBJ
): WatchStopHandle {
// 1. 标准化 source 为 getter 函数
const getter = () => source()
// 2. 创建调度器(控制回调执行时机)
let scheduler: (job: () => void) => void
if (flush === 'sync') {
scheduler = (job) => job() // 同步执行
} else if (flush === 'pre') {
scheduler = (job) => queueJob(job) // 组件更新前执行
} else {
scheduler = (job) => queuePostFlushCb(job) // 微任务队列执行(默认)
}
// 3. 创建 ReactiveEffect
const effect = new ReactiveEffect(getter, scheduler)
// 4. 保存旧值并定义回调
let oldValue: any
const job = () => {
if (cb) {
const newValue = effect.run()
if (deep || hasChanged(newValue, oldValue)) {
cb(newValue, oldValue)
oldValue = newValue
}
}
}
// 5. 初始化和依赖收集
oldValue = effect.run()
// 返回停止监听的函数
return () => effect.stop()
}
3. 关键流程
-
标准化
source:将监听源转换为getter函数(如() => obj.prop)。 -
创建调度器:
-
默认使用微任务队列(
queuePostFlushCb),确保回调在 DOM 更新后执行。 -
支持
sync(同步)、pre(组件更新前)等模式。
-
-
创建
ReactiveEffect:-
getter作为副作用函数,内部访问的响应式数据会被自动收集为依赖。 -
依赖变化时触发调度器,而非直接运行
getter。
-
-
回调执行:
-
当依赖变化时,调度器将
job推入队列。 -
job中对比新旧值,若变化则执行用户回调。
二、computed里面的trigger,track都是什么,讲下源码实现
一、track 和 trigger 的作用
-
track-
依赖收集:当一个响应式对象的属性被访问(例如在
computed的getter中访问另一个ref),Vue 会记录当前正在运行的effect(副作用函数),并建立依赖关系。 -
数据存储:所有依赖关系会被存储在一个全局的
WeakMap(targetMap)中,结构为:target -> key -> Set<effects>。
-
-
trigger-
触发更新:当响应式对象的属性被修改时,Vue 会根据
targetMap找到所有依赖该属性的effect,并重新执行它们(例如computed的重新计算或组件的重新渲染)。
-
二、源码实现分析
2. track 的实现
当访问响应式对象的属性时,调用 track 收集当前 effect:
// packages/reactivity/src/effect.ts
export function track(target: object, type: TrackOpTypes, key: unknown) {
if (!shouldTrack || activeEffect === undefined) return
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
if (!dep.has(activeEffect)) {
dep.add(activeEffect) // 将当前 effect 添加到依赖集合
activeEffect.deps.push(dep) // 反向关联,用于清理依赖
}
}
关键逻辑:
-
activeEffect是当前正在运行的effect(例如computed的getter函数对应的effect)。 -
将
activeEffect添加到target.key对应的依赖集合dep中。 -
反向关联:
effect.deps会记录所有依赖它的dep,用于后续清理(如effect停止时)。
3. trigger 的实现
当修改响应式对象的属性时,调用 trigger 触发依赖更新:
// packages/reactivity/src/effect.ts
export function trigger(
target: object,
type: TriggerOpTypes,
key?: unknown
) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const effects = new Set<ReactiveEffect>()
// 收集所有需要触发的 effect
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
if (effectsToAdd) {
effectsToAdd.forEach(effect => {
if (effect !== activeEffect || effect.allowRecurse) {
effects.add(effect)
}
})
}
}
// 根据操作类型处理不同情况
if (type === TriggerOpTypes.CLEAR) {
// 清空整个对象的依赖
depsMap.forEach(add)
} else if (key !== undefined) {
// 触发特定 key 的依赖
add(depsMap.get(key))
}
// 触发 effect 的调度执行
effects.forEach(effect => {
if (effect.scheduler) {
effect.scheduler() // 优先执行自定义调度器(如 computed 的异步更新)
} else {
effect.run() // 直接运行 effect
}
})
}

浙公网安备 33010602011771号