Vue3 响应式核心
这部分是整个 Vue3 的灵魂,比 VDOM 更重要。 我会带你从概念 → 机制 → 数据结构 → 源码流程 → 实现 mini-reactive,达到可直接读 Vue 源码的程度。
# 🍀 1. Vue3 响应式的核心思想(Proxy-based)
Vue2 用 Object.defineProperty,有硬伤:
- 无法监听数组下标
- 无法监听对象新增属性
- 深层对象依赖难以追踪
- 性能受大量 getter/setter 影响
Vue3 改为 Proxy,解决所有上述痛点,并支持:
- ✔ 深度监听
- ✔ 数组 / Map / Set 原生监听
- ✔ 惰性访问
- ✔ WeakMap 缓存,提升性能
核心结构:
state → Proxy(wrapper) → get → track → effect → trigger → update
# 🔥 2. 响应式的重要组成部分(最核心的三块)
Vue3 reactivity 包包含三大核心:
# ① effect()
作用:注册副作用函数(例如组件 render)
本质是:
把当前执行的函数登记为“依赖”,当数据变动时重新执行它。
# ② track()
作用:依赖收集 触发场景:getter
当你访问:
state.count
Vue3 会调用:
track(target, key)
将当前活跃的 effect 绑定到这个 key 上。
# ③ trigger()
作用:派发更新 触发场景:setter
当执行:
state.count++
Vue 会调用:
trigger(target, key)
触发所有依赖这个 key 的 effect 重新执行(更新视图)。
# 🧠 3. Vue3 响应式数据结构(非常重要)
依赖收集关系如下:
targetMap = WeakMap()
↓
target (state 对象)
↓
depsMap = Map()
↓
key → Dep(Set of effect)
2
3
4
5
6
7
可以画成这样:
WeakMap
└── target1 (reactive 对象)
└── Map
├── key1 → Set(effectA, effectB)
├── key2 → Set(effectC)
└── target2
└── ...
2
3
4
5
6
7
解释:
- 每个 reactive 对象是一个
target - 每个 key 对应一个依赖集合
Dep - Dep 是一个 Set,里面装多个 effect
# 🎯 4. reactive() 的实现机制
Vue3 的 reactive() 内部创建一个 Proxy:
function reactive(target) {
return new Proxy(target, mutableHandlers)
}
2
3
其中最关键的是 get/set:
# get → track
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver)
track(target, key)
return res
}
2
3
4
5
# set → trigger
set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
trigger(target, key)
}
return result
}
2
3
4
5
6
7
8
9
10
Proxy 的 handler 中只有两件事:
get → track set → trigger
# 🚀 5. effect 核心逻辑(依赖收集的大脑)
effect(fn) 会:
- 把 fn 存到全局变量 activeEffect
- 执行 fn()
- track 时把 activeEffect 收集进去
源码伪代码:
let activeEffect = null
export function effect(fn) {
const effectFn = () => {
activeEffect = effectFn
fn()
activeEffect = null
}
effectFn()
return effectFn
}
2
3
4
5
6
7
8
9
10
11
# ⚡ 6. track 实现(依赖收集)
function track(target, 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)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
总结: 访问数据 → 把当前 effect 加到依赖表里
# 💥 7. trigger 实现(派发更新)
当数据变动时:
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
dep.forEach(effectFn => effectFn())
}
}
2
3
4
5
6
7
8
9
本质是:
key 绑定的所有 effect 都重新执行(重新渲染)
# 🧨 8. 依赖追踪如何避免死循环?
Vue 3 使用 “调度器 scheduler” 来避免 effect 递归触发:
effect(fn, {
scheduler(job) {
queueJob(job)
}
})
2
3
4
5
一般组件渲染的 effect 都会经过 scheduler,从而批量更新、避免重复渲染。
# 📌 9. ref() 的本质
ref() 也是 reactive 的一层封装:
export function ref(value) {
const wrapper = {
value
}
return reactive(wrapper)
}
2
3
4
5
6
也就是说:
const count = ref(0)
等价于:
reactive({ value: 0 })
访问 count.value → get → track 修改 count.value → set → trigger
# 📦 10. computed 的内部机制(很巧妙)
computed 使用 lazy effect + 缓存:
伪代码:
function computed(getter) {
let dirty = true
let value
const effectFn = effect(getter, {
lazy: true,
scheduler() {
dirty = true
}
})
return {
get value() {
if (dirty) {
value = effectFn()
dirty = false
}
return value
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
computed 特性:
- getter 内部依赖变化 → dirty = true
- 下次访问 value 时才重新计算
- 没访问就不计算(惰性)
非常高效。
# 🏆 11. 响应式完整流程(超清晰)
# 第一次渲染:
- effect(render)
- render 中访问 state.xxx → track
- 建立依赖关系
# 数据变化:
- state.xxx = newValue → trigger
- 找到所有 effect
- scheduler → queueJob
- 重新渲染(effect 执行)
- patch → 更新 DOM
流程图:
get → track → 收集依赖
set → trigger → effect → patch → DOM 更新
2
# 🔥 12. 你可以直接运行的 mini-reactive 实现(核心 30 行)
let activeEffect = null
const targetMap = new WeakMap()
function effect(fn) {
const effectFn = () => {
activeEffect = effectFn
fn()
activeEffect = null
}
effectFn()
}
function track(target, key) {
if (!activeEffect) 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()))
dep.add(activeEffect)
}
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) dep.forEach(fn => fn())
}
function reactive(target) {
return new Proxy(target, {
get(t, k, r) {
const res = Reflect.get(t, k, r)
track(t, k)
return res
},
set(t, k, v, r) {
const old = t[k]
const result = Reflect.set(t, k, v, r)
if (old !== v) trigger(t, k)
return result
}
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
你可以用它:
const state = reactive({ count: 0 })
effect(() => {
console.log("count changed:", state.count)
})
state.count++
2
3
4
5
6
7
# 🎓 13. 到这你已经掌握:
- ✔ reactive 原理
- ✔ effect / track / trigger
- ✔ WeakMap → Map → Set 数据结构
- ✔ ref/computed 内部机制
- ✔ scheduler 调度系统
- ✔ Proxy 拦截逻辑
已经达到可以看 Vue3 源码的级别。