🧬 Vue 3 响应式核心源码详解(基于 @vue/reactivity)
⚙️ 整理不易,记得点赞、收藏、关注,揭开 Vue 响应式的神秘面纱!
🧭 一、源码结构总览(relevant files)
Vue 的响应式系统主要在 @vue/reactivity
包中,核心源码文件包括:
文件名 | 作用说明 |
---|---|
reactive.ts | 创建响应式对象的入口 |
ref.ts | 实现 ref 响应式数据 |
effect.ts | 实现副作用追踪(依赖收集与触发) |
computed.ts | 实现 computed 的缓存逻辑 |
watch.ts | 实现 watch 响应逻辑 |
baseHandlers.ts | 代理对象的拦截逻辑(get/set) |
reactiveEffect.ts | 核心依赖收集机制(调度系统) |
我们将通过 执行流程 + 源码解构 + 关键机制 三个部分讲透它👇
🔍 二、响应式系统的运行流程(大局观)
- 使用
reactive(obj)
或ref(value)
创建响应式数据; - 使用响应式数据的地方(如组件、computed)注册为“副作用函数”
ReactiveEffect
; - 数据被访问时会收集依赖(track);
- 数据被修改时会触发依赖(trigger);
- 依赖更新后触发副作用函数(如组件更新、watch 回调、computed 重算)。
🧠 本质上,是一个“数据和函数之间的订阅-发布机制”。
🧪 三、关键源码拆解
1️⃣ reactive 的本质:Proxy 包裹对象
export function reactive(target: object): object {return createReactiveObject(target, false, mutableHandlers)
}
实际调用的是 createReactiveObject
,它的核心逻辑:
function createReactiveObject(target, isReadonly, baseHandlers) {const proxy = new Proxy(target, baseHandlers)return proxy
}
配合 mutableHandlers.ts
中的 get
拦截器:
get(target, key, receiver) {const res = Reflect.get(target, key, receiver)track(target, 'get', key) // 依赖收集return isObject(res) ? reactive(res) : res
}
📌 重点:每次读取属性,会调用 track()
做依赖收集!
2️⃣ ref 的本质:包裹值 + 自定义 getter/setter
export function ref(value) {return createRef(value)
}
function createRef(rawValue) {const r = {get value() {track(r, 'get', 'value') // 收集依赖return rawValue},set value(newVal) {rawValue = newValtrigger(r, 'set', 'value') // 触发更新}}return r
}
✅ ref 是通过
getter/setter
控制单值的响应式行为。
3️⃣ track:收集依赖
const targetMap = new WeakMap()export function track(target, type, key) {if (!activeEffect) returnlet depsMap = targetMap.get(target)if (!depsMap) {depsMap = new Map()targetMap.set(target, depsMap)}let dep = depsMap.get(key)if (!dep) {dep = new Set()depsMap.set(key, dep)}dep.add(activeEffect) // 绑定副作用函数
}
每个对象的 key -> Set(effect),形成完整依赖图。
4️⃣ trigger:触发依赖
export function trigger(target, type, key) {const depsMap = targetMap.get(target)if (!depsMap) returnconst effects = depsMap.get(key)if (effects) {effects.forEach(effect => {effect()})}
}
数据变了,就找到 key 对应的 effect 执行回调!
5️⃣ ReactiveEffect 类:副作用的封装载体
export class ReactiveEffect {constructor(fn, scheduler) {this.fn = fnthis.scheduler = scheduler}run() {activeEffect = thisreturn this.fn()}
}
用于封装副作用函数,例如 watch
、computed
、组件更新逻辑等。
6️⃣ computed 的实现:带缓存的 ReactiveEffect
export function computed(getter) {let valuelet dirty = trueconst effect = new ReactiveEffect(getter, () => {dirty = truetrigger(obj, 'set', 'value')})const obj = {get value() {if (dirty) {value = effect.run()dirty = false}track(obj, 'get', 'value')return value}}return obj
}
- 懒执行:只有在
.value
被访问时才执行 - 缓存机制:依赖没变不会重新执行 getter
7️⃣ watch 的实现:注册一个副作用函数,包裹 source
export function watch(source, cb, options?) {let getter = () => source.value // 简化const job = () => {const newVal = effect.run()cb(newVal, oldVal)oldVal = newVal}const effect = new ReactiveEffect(getter, job)if (options.immediate) job()else oldVal = effect.run()
}
- 自动依赖收集
- 值变化后执行 job 调用回调
📦 四、响应式系统核心图示总结
+-------------------------+| reactive/ref |+-------------------------+|↓ Proxy or Getter|+--------------+| track() | ← 收集依赖+--------------+|+--------------+| trigger() | → 执行副作用+--------------+|+---------------------------+| ReactiveEffect(fn) |+---------------------------+↑ ↓run() scheduler(watch/computed)
🧠 五、总结一下
机制 | 功能说明 |
---|---|
reactive | 用 Proxy 代理对象,拦截 get/set 实现响应式 |
ref | 定义 .value 属性,包裹单值响应式 |
track | 收集依赖到 effect |
trigger | 执行依赖的 effect |
ReactiveEffect | 封装副作用函数 |
computed | 带缓存的懒执行响应式副作用 |
watch | 主动监听响应式数据变化,执行回调 |