🧬 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 = newVal
trigger(r, 'set', 'value') // 触发更新
}
}
return r
}
✅ ref 是通过
getter/setter
控制单值的响应式行为。
3️⃣ track:收集依赖
const targetMap = new WeakMap()
export function track(target, type, key) {
if (!activeEffect) return
let 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) return
const effects = depsMap.get(key)
if (effects) {
effects.forEach(effect => {
effect()
})
}
}
数据变了,就找到 key 对应的 effect 执行回调!
5️⃣ ReactiveEffect 类:副作用的封装载体
export class ReactiveEffect {
constructor(fn, scheduler) {
this.fn = fn
this.scheduler = scheduler
}
run() {
activeEffect = this
return this.fn()
}
}
用于封装副作用函数,例如 watch
、computed
、组件更新逻辑等。
6️⃣ computed 的实现:带缓存的 ReactiveEffect
export function computed(getter) {
let value
let dirty = true
const effect = new ReactiveEffect(getter, () => {
dirty = true
trigger(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 |
主动监听响应式数据变化,执行回调 |