🧬 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 | 主動監聽響應式數據變化,執行回調 |