(第三章) Vue3 响应式原理深度解析【图解+源码】
📅 发布日期:2025年10月24日
🏷️ 标签:Vue3、响应式、Proxy、Reflect、原理
⏱️ 阅读时长:约125分钟
💡 难度:⭐⭐⭐ 高级
📖 前言
大家好,我是【代码小库】的做者。这是 Vue3 实战系列 的第3篇文章。
在前两篇中,我们学习了项目搭建和 Composition API。今天,我们将深入 Vue3 的响应式系统原理,理解数据驱动视图更新的底层机制。
本文你将学到:
- ✅ Vue2 和 Vue3 响应式系统的区别
- ✅ Proxy 和 Reflect 的核心 API 和使用
- ✅ Vue3 响应式系统的实现原理
- ✅ 依赖收集(track)和派发更新(trigger)机制
- ✅ 手写一个简易的响应式系统
- ✅ 常见问题和性能优化
🔄 一、Vue2 vs Vue3 响应式系统对比
1.1 Vue2 的响应式实现(Object.defineProperty)
Vue2 使用 Object.defineProperty 实现响应式:
// Vue2 的实现方式
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}: ${val}`)
// 收集依赖
return val
},
set(newVal) {
if (newVal !== val) {
console.log(`设置 ${key}: ${newVal}`)
val = newVal
// 触发更新
}
}
})
}
// 使用
const obj = {}
defineReactive(obj, 'name', 'John')
console.log(obj.name) // 读取 name: John
obj.name = 'Tom' // 设置 name: Tom
Vue2 的局限性:
const obj = { name: 'John' }
// ❌ 问题1:无法检测新增属性
obj.age = 25 // 不会触发视图更新
// 解决:需要使用 Vue.set(obj, 'age', 25)
// ❌ 问题2:无法检测删除属性
delete obj.name // 不会触发视图更新
// 解决:需要使用 Vue.delete(obj, 'name')
// ❌ 问题3:数组索引修改无法检测
const arr = [1, 2, 3]
arr[0] = 100 // 不会触发视图更新
// 解决:需要使用 Vue.set(arr, 0, 100) 或 arr.splice(0, 1, 100)
// ❌ 问题4:数组长度修改无法检测
arr.length = 0 // 不会触发视图更新
1.2 Vue3 的响应式实现(Proxy)
Vue3 使用 Proxy 实现响应式,完美解决了 Vue2 的所有问题:
const obj = new Proxy(
{ name: 'John' },
{
get(target, key) {
console.log(`读取 ${key}: ${target[key]}`)
return target[key]
},
set(target, key, newValue) {
console.log(`设置 ${key}: ${newValue}`)
target[key] = newValue
return true
},
deleteProperty(target, key) {
console.log(`删除 ${key}`)
delete target[key]
return true
}
}
)
// ✅ 新增属性 - 可以检测
obj.age = 25 // 设置 age: 25
// ✅ 删除属性 - 可以检测
delete obj.name // 删除 name
// ✅ 数组操作 - 全都可以检测
const arr = new Proxy([1, 2, 3], {
set(target, key, newValue) {
console.log(`设置 arr[${key}] = ${newValue}`)
target[key] = newValue
return true
}
})
arr[0] = 100 // 设置 arr[0] = 100
arr.length = 0 // 设置 arr[length] = 0
Vue3 的优势:
| 特性 | Vue2 (Object.defineProperty) | Vue3 (Proxy) |
|---|---|---|
| 新增属性 | ❌ 需要Vue.set |
✅ 自动检测 |
| 删除属性 | ❌ 需要Vue.delete |
✅ 自动检测 |
| 数组索引修改 | ❌ 需要特殊处理 | ✅ 自动检测 |
| 数组长度修改 | ❌ 需要特殊处理 | ✅ 自动检测 |
| 性能 | ⭐⭐⭐ 需要遍历所有属性 | ⭐⭐⭐⭐⭐ 懒代理 |
| 浏览器兼容性 | ✅ IE9+ | ⚠️ 不支持 IE11 |
🎯 二、Proxy 和 Reflect 基础
2.1 Proxy 完整 API
const proxy = new Proxy(target, handler)
handler 支持的拦截操作:
const handler = {
// 1. 读取属性
get(target, key, receiver) {
console.log(`读取 ${key}`)
return Reflect.get(target, key, receiver)
},
// 2. 设置属性
set(target, key, value, receiver) {
console.log(`设置 ${key} = ${value}`)
return Reflect.set(target, key, value, receiver)
},
// 3. 检查属性是否存在 (in 操作符)
has(target, key) {
console.log(`检查 ${key} 是否存在`)
return Reflect.has(target, key)
},
// 4. 删除属性 (delete 操作符)
deleteProperty(target, key) {
console.log(`删除 ${key}`)
return Reflect.deleteProperty(target, key)
},
// 5. 获取自身属性键 (Object.keys)
ownKeys(target) {
console.log('获取所有键')
return Reflect.ownKeys(target)
},
// 6. 获取属性描述符
getOwnPropertyDescriptor(target, key) {
return Reflect.getOwnPropertyDescriptor(target, key)
},
// 7. 定义属性
defineProperty(target, key, descriptor) {
return Reflect.defineProperty(target, key, descriptor)
},
// 8. 获取原型
getPrototypeOf(target) {
return Reflect.getPrototypeOf(target)
},
// 9. 设置原型
setPrototypeOf(target, proto) {
return Reflect.setPrototypeOf(target, proto)
},
// 10. 检查对象是否可扩展
isExtensible(target) {
return Reflect.isExtensible(target)
},
// 11. 阻止对象扩展
preventExtensions(target) {
return Reflect.preventExtensions(target)
},
// 12. 函数调用拦截 (apply)
apply(target, thisArg, argsList) {
console.log('函数被调用')
return Reflect.apply(target, thisArg, argsList)
},
// 13. new 操作符拦截
construct(target, argsList, newTarget) {
console.log('构造函数被调用')
return Reflect.construct(target, argsList, newTarget)
}
}
2.2 Reflect 的作用
为什么使用 Reflect?
const obj = { name: 'John' }
// ❌ 直接操作 - 可能出错
try {
Object.defineProperty(obj, 'age', { value: 25 })
} catch (error) {
console.error('操作失败')
}
// ✅ 使用 Reflect - 返回布尔值表示成功/失败
const success = Reflect.defineProperty(obj, 'age', { value: 25 })
if (!success) {
console.error('操作失败')
}
Reflect 的三大优势:
- 函数式编程风格 - 所有操作都返回有意义的值
- 与 Proxy 一一对应 - 更统一的 API
- 更合理的返回值 - 布尔值表示操作成功/失败
实例:Proxy + Reflect 结合使用
const person = {
name: 'John',
age: 25
}
const proxy = new Proxy(person, {
get(target, key, receiver) {
console.log(`读取 ${key}`)
// receiver 指向代理对象,确保 this 指向正确
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log(`设置 ${key} = ${value}`)
// 验证:age 必须是数字
if (key === 'age' && typeof value !== 'number') {
throw new TypeError('age 必须是数字')
}
return Reflect.set(target, key, value, receiver)
}
})
// 使用
proxy.name = 'Tom' // 设置 name = Tom
proxy.age = 30 // 设置 age = 30
proxy.age = '30' // 抛出错误: age 必须是数字
🔧 三、Vue3 响应式系统核心实现
3.1 核心数据结构
// 依赖收集的核心数据结构
type Dep = Set<ReactiveEffect> // 依赖集合
type KeyToDepMap = Map<any, Dep> // 键 -> 依赖集合
const targetMap = new WeakMap<any, KeyToDepMap>() // 对象 -> 键映射
// 响应式副作用函数
interface ReactiveEffect {
(): void
deps: Dep[] // 该 effect 依赖的所有 dep
}
// 当前正在执行的 effect
let activeEffect: ReactiveEffect | undefined
数据结构关系:
targetMap (WeakMap)
├─ target1 (原始对象) → depsMap (Map)
│ ├─ key1 → dep (Set) → [effect1, effect2, ...]
│ ├─ key2 → dep (Set) → [effect3, ...]
│ └─ key3 → dep (Set) → [effect1, effect4, ...]
│
└─ target2 (原始对象) → depsMap (Map)
├─ key1 → dep (Set) → [effect5, ...]
└─ key2 → dep (Set) → [effect6, effect7, ...]
3.2 reactive - 创建响应式对象
/**
* 创建响应式对象
*/
function reactive<T extends object>(target: T): T {
// 如果已经是代理对象,直接返回
if (isProxy(target)) {
return target
}
return new Proxy(target, {
get(target, key, receiver) {
// 特殊标记:判断是否是响应式对象
if (key === '__v_isReactive') {
return true
}
const result = Reflect.get(target, key, receiver)
// 依赖收集
track(target, key)
// 如果属性值是对象,递归创建响应式对象(懒代理)
if (isObject(result)) {
return reactive(result)
}
return result
},
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
},
deleteProperty(target, key) {
// 检查属性是否存在
const hadKey = hasOwn(target, key)
const result = Reflect.deleteProperty(target, key)
// 如果成功删除了存在的属性,触发更新
if (result && hadKey) {
trigger(target, key)
}
return result
}
})
}
// 工具函数
function isObject(val: any): boolean {
return val !== null && typeof val === 'object'
}
function hasOwn(target: object, key: string | symbol): boolean {
return Object.prototype.hasOwnProperty.call(target, key)
}
function isProxy(value: any): boolean {
return !!(value && value.__v_isReactive)
}
3.3 ref - 创建响应式引用
/**
* ref 的实现
*/
class RefImpl<T> {
private _value: T
public dep: Dep = new Set()
public readonly __v_isRef = true
constructor(private _rawValue: T) {
// 如果是对象,转换为 reactive
this._value = convert(_rawValue)
}
get value() {
// 依赖收集
trackRefValue(this)
return this._value
}
set value(newValue: T) {
// 只有值确实改变了才更新
if (hasChanged(newValue, this._rawValue)) {
this._rawValue = newValue
this._value = convert(newValue)
// 触发更新
triggerRefValue(this)
}
}
}
function ref<T>(value: T) {
return new RefImpl(value)
}
// 转换函数:对象转 reactive,基本类型直接返回
function convert<T>(value: T): T {
return isObject(value) ? reactive(value) : value
}
// 判断值是否改变
function hasChanged(value: any, oldValue: any): boolean {
return !Object.is(value, oldValue)
}
3.4 track - 依赖收集
/**
* 依赖收集
* @param target 原始对象
* @param key 属性键
*/
function track(target: object, key: unknown) {
// 如果没有 activeEffect,说明不是在 effect 中访问的,不需要收集
if (!activeEffect) {
return
}
// 1. 从 targetMap 中获取该对象的 depsMap
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
// 2. 从 depsMap 中获取该属性的 dep
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
// 3. 将当前 effect 添加到 dep 中
if (!dep.has(activeEffect)) {
dep.add(activeEffect)
// 双向记录:effect 也记录它依赖的 dep
activeEffect.deps.push(dep)
}
}
/**
* ref 的依赖收集
*/
function trackRefValue(ref: RefImpl<any>) {
if (activeEffect) {
trackEffects(ref.dep)
}
}
function trackEffects(dep: Dep) {
if (!dep.has(activeEffect!)) {
dep.add(activeEffect!)
activeEffect!.deps.push(dep)
}
}
3.5 trigger - 派发更新
/**
* 派发更新
* @param target 原始对象
* @param key 属性键
*/
function trigger(target: object, key: unknown) {
// 1. 获取该对象的 depsMap
const depsMap = targetMap.get(target)
if (!depsMap) {
return
}
// 2. 获取该属性的 dep
const dep = depsMap.get(key)
if (!dep) {
return
}
// 3. 执行所有 effect
triggerEffects(dep)
}
/**
* 执行所有 effect
*/
function triggerEffects(dep: Dep) {
// 创建副本,避免在遍历时修改 Set
const effects = [...dep]
effects.forEach((effect) => {
// 如果 effect 有 scheduler,执行 scheduler 而不是 effect 本身
if (effect.scheduler) {
effect.scheduler()
} else {
effect()
}
})
}
/**
* ref 的派发更新
*/
function triggerRefValue(ref: RefImpl<any>) {
triggerEffects(ref.dep)
}
3.6 effect - 副作用函数
/**
* 创建副作用函数
*/
function effect<T = any>(
fn: () => T,
options?: {
lazy?: boolean
scheduler?: (effect: ReactiveEffect) => void
}
) {
const _effect = function reactiveEffect() {
// 清除之前的依赖
cleanup(_effect)
// 设置为当前活跃的 effect
activeEffect = _effect
// 执行原始函数
const result = fn()
// 重置
activeEffect = undefined
return result
} as ReactiveEffect
_effect.deps = []
_effect.scheduler = options?.scheduler
// 如果不是 lazy,立即执行
if (!options?.lazy) {
_effect()
}
return _effect
}
/**
* 清除 effect 的所有依赖
*/
function cleanup(effect: ReactiveEffect) {
const { deps } = effect
if (deps.length) {
for (let i = 0; i < deps.length; i++) {
deps[i].delete(effect)
}
deps.length = 0
}
}
🎬 四、响应式系统工作流程
4.1 完整流程示例
import { reactive, effect } from './reactivity'
// 1. 创建响应式对象
const state = reactive({
count: 0,
message: 'Hello'
})
// 2. 创建副作用函数(会自动执行一次)
effect(() => {
console.log(`count: ${state.count}`)
console.log(`message: ${state.message}`)
})
// 输出:
// count: 0
// message: Hello
// 3. 修改 count(触发更新)
state.count = 1
// 输出:
// count: 1
// message: Hello
// 4. 修改 message(触发更新)
state.message = 'Hi'
// 输出:
// count: 1
// message: Hi
执行流程分析:
1. 执行 reactive(obj)
└─> 创建 Proxy 对象
2. 执行 effect(() => { console.log(state.count) })
├─> 执行 effect 函数
├─> 设置 activeEffect = effect
├─> 执行回调函数
│ └─> 访问 state.count
│ └─> 触发 get 拦截
│ └─> 执行 track(state, 'count')
│ └─> 收集依赖: targetMap.set(state, { count: Set([effect]) })
└─> 重置 activeEffect = undefined
3. 执行 state.count = 1
├─> 触发 set 拦截
└─> 执行 trigger(state, 'count')
└─> 从 targetMap 中找到 effect
└─> 执行 effect() (重新运行回调函数)
└─> 输出: count: 1
4.2 嵌套 effect 的处理
const state = reactive({ count: 0, double: 0 })
// 外层 effect
effect(() => {
console.log('外层 effect')
// 内层 effect
effect(() => {
console.log('内层 effect')
state.double = state.count * 2
})
console.log(`count: ${state.count}`)
})
// 输出:
// 外层 effect
// 内层 effect
// count: 0
state.count = 1
// 输出:
// 内层 effect (内层 effect 重新执行)
// 外层 effect (外层 effect 重新执行)
// 内层 effect (外层 effect 中的内层 effect 重新执行)
// count: 1
解决嵌套问题:使用 effect 栈
const effectStack: ReactiveEffect[] = []
let activeEffect: ReactiveEffect | undefined
function effect(fn: Function) {
const _effect = function reactiveEffect() {
cleanup(_effect)
// 压入栈
effectStack.push(_effect)
activeEffect = _effect
const result = fn()
// 弹出栈
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
return result
} as ReactiveEffect
_effect.deps = []
_effect()
return _effect
}
🎨 五、computed - 计算属性的实现
class ComputedRefImpl<T> {
private _value: T
private _dirty = true // 是否需要重新计算
public readonly effect: ReactiveEffect
public readonly dep: Dep = new Set()
public readonly __v_isRef = true
constructor(getter: () => T) {
// 创建 effect,但不立即执行(lazy: true)
this.effect = effect(getter, {
lazy: true,
scheduler: () => {
// 当依赖变化时,标记为 dirty
if (!this._dirty) {
this._dirty = true
// 触发依赖这个 computed 的 effect
triggerRefValue(this)
}
}
})
}
get value() {
// 依赖收集
trackRefValue(this)
// 只有 dirty 时才重新计算
if (this._dirty) {
this._dirty = false
this._value = this.effect()
}
return this._value
}
}
function computed<T>(getter: () => T) {
return new ComputedRefImpl(getter)
}
使用示例:
const state = reactive({ count: 0 })
const double = computed(() => {
console.log('计算 double')
return state.count * 2
})
console.log(double.value) // 计算 double, 输出: 0
console.log(double.value) // 不重新计算,直接返回缓存值 0
state.count = 1
console.log(double.value) // 计算 double, 输出: 2
console.log(double.value) // 不重新计算,直接返回缓存值 2
computed 的优势:
- ✅ 懒计算 - 只有访问时才计算
- ✅ 缓存 - 依赖不变时不重新计算
- ✅ 自动依赖追踪 - 自动收集依赖
🎯 六、watch - 侦听器的实现
function watch<T>(
source: () => T | T,
callback: (newValue: T, oldValue: T) => void,
options?: {
immediate?: boolean
deep?: boolean
}
) {
// 统一处理 source
let getter: () => T
if (typeof source === 'function') {
getter = source as () => T
} else {
// 如果是 ref,转换为 getter
getter = () => (source as any).value
}
let oldValue: T
// 深度遍历(用于 deep 选项)
const traverse = (value: any, seen = new Set()) => {
if (!isObject(value) || seen.has(value)) {
return value
}
seen.add(value)
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
traverse(value[i], seen)
}
} else {
for (const key in value) {
traverse(value[key], seen)
}
}
return value
}
// 包装 getter(处理 deep 选项)
const wrappedGetter = options?.deep
? () => traverse(getter())
: getter
// 创建 effect
const job = () => {
const newValue = wrappedGetter()
callback(newValue, oldValue)
oldValue = newValue
}
const effectFn = effect(wrappedGetter, {
lazy: true,
scheduler: job
})
// immediate 选项:立即执行
if (options?.immediate) {
job()
} else {
oldValue = effectFn()
}
}
使用示例:
const state = reactive({ count: 0 })
// 基本使用
watch(
() => state.count,
(newValue, oldValue) => {
console.log(`count 从 ${oldValue} 变成了 ${newValue}`)
}
)
state.count = 1 // 输出: count 从 0 变成了 1
// immediate 选项
watch(
() => state.count,
(newValue, oldValue) => {
console.log(`当前 count: ${newValue}`)
},
{ immediate: true }
)
// 立即输出: 当前 count: 1
// deep 选项
const state2 = reactive({
user: {
name: 'John',
age: 25
}
})
watch(
() => state2.user,
(newValue) => {
console.log('user 变化了', newValue)
},
{ deep: true }
)
state2.user.age = 26 // 触发侦听器
💡 七、完整的响应式系统实现
现在,让我们整合所有代码,实现一个完整的迷你响应式系统:
// ============ 类型定义 ============
type Dep = Set<ReactiveEffect>
type KeyToDepMap = Map<any, Dep>
interface ReactiveEffect {
(): any
deps: Dep[]
scheduler?: (effect: ReactiveEffect) => void
}
// ============ 全局变量 ============
const targetMap = new WeakMap<any, KeyToDepMap>()
const effectStack: ReactiveEffect[] = []
let activeEffect: ReactiveEffect | undefined
// ============ 工具函数 ============
function isObject(val: any): boolean {
return val !== null && typeof val === 'object'
}
function hasChanged(value: any, oldValue: any): boolean {
return !Object.is(value, oldValue)
}
// ============ reactive 实现 ============
function reactive<T extends object>(target: T): T {
return new Proxy(target, {
get(target, key, receiver) {
if (key === '__v_isReactive') {
return true
}
const result = Reflect.get(target, key, receiver)
track(target, key)
if (isObject(result)) {
return reactive(result)
}
return result
},
set(target, key, value, receiver) {
const oldValue = target[key]
const result = Reflect.set(target, key, value, receiver)
if (hasChanged(value, oldValue)) {
trigger(target, key)
}
return result
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
trigger(target, key)
return result
}
})
}
// ============ ref 实现 ============
class RefImpl<T> {
private _value: T
public dep: Dep = new Set()
public readonly __v_isRef = true
constructor(private _rawValue: T) {
this._value = isObject(_rawValue) ? reactive(_rawValue) : _rawValue
}
get value() {
trackEffects(this.dep)
return this._value
}
set value(newValue: T) {
if (hasChanged(newValue, this._rawValue)) {
this._rawValue = newValue
this._value = isObject(newValue) ? reactive(newValue) : newValue
triggerEffects(this.dep)
}
}
}
function ref<T>(value: T) {
return new RefImpl(value)
}
// ============ track / trigger ============
function track(target: object, key: unknown) {
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()))
}
trackEffects(dep)
}
function trackEffects(dep: Dep) {
if (activeEffect && !dep.has(activeEffect)) {
dep.add(activeEffect)
activeEffect.deps.push(dep)
}
}
function trigger(target: object, key: unknown) {
const depsMap = targetMap.get(target)
if (!depsMap) return
const dep = depsMap.get(key)
if (dep) {
triggerEffects(dep)
}
}
function triggerEffects(dep: Dep) {
const effects = [...dep]
effects.forEach((effect) => {
if (effect.scheduler) {
effect.scheduler(effect)
} else {
effect()
}
})
}
// ============ effect 实现 ============
function cleanup(effect: ReactiveEffect) {
const { deps } = effect
if (deps.length) {
for (let i = 0; i < deps.length; i++) {
deps[i].delete(effect)
}
deps.length = 0
}
}
function effect<T = any>(
fn: () => T,
options?: {
lazy?: boolean
scheduler?: (effect: ReactiveEffect) => void
}
) {
const _effect = function reactiveEffect() {
cleanup(_effect)
effectStack.push(_effect)
activeEffect = _effect
const result = fn()
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
return result
} as ReactiveEffect
_effect.deps = []
_effect.scheduler = options?.scheduler
if (!options?.lazy) {
_effect()
}
return _effect
}
// ============ computed 实现 ============
class ComputedRefImpl<T> {
private _value: T
private _dirty = true
public readonly effect: ReactiveEffect
public readonly dep: Dep = new Set()
public readonly __v_isRef = true
constructor(getter: () => T) {
this.effect = effect(getter, {
lazy: true,
scheduler: () => {
if (!this._dirty) {
this._dirty = true
triggerEffects(this.dep)
}
}
})
}
get value() {
trackEffects(this.dep)
if (this._dirty) {
this._dirty = false
this._value = this.effect()
}
return this._value
}
}
function computed<T>(getter: () => T) {
return new ComputedRefImpl(getter)
}
// ============ 导出 ============
export { reactive, ref, effect, computed, track, trigger }
完整示例:
// 使用我们实现的响应式系统
const state = reactive({
count: 0,
user: {
name: 'John',
age: 25
}
})
// 计算属性
const double = computed(() => state.count * 2)
// 副作用函数
effect(() => {
console.log(`count: ${state.count}, double: ${double.value}`)
console.log(`user.name: ${state.user.name}`)
})
// 输出:
// count: 0, double: 0
// user.name: John
// 修改数据
state.count = 1
// 输出:
// count: 1, double: 2
// user.name: John
state.user.name = 'Tom'
// 输出:
// count: 1, double: 2
// user.name: Tom
// 新增属性(Proxy 可以检测到)
state.user.email = 'tom@example.com'
// 输出:
// count: 1, double: 2
// user.name: Tom
⚡ 八、性能优化
8.1 避免不必要的依赖收集
const state = reactive({ count: 0, message: 'Hello' })
// ❌ 不好:即使不访问 message,也会收集依赖
effect(() => {
if (state.count > 0) {
console.log(state.message)
}
})
// ✅ 好:只在需要时收集依赖
effect(() => {
const count = state.count
if (count > 0) {
console.log(state.message)
}
})
8.2 使用 shallowReactive 减少深度代理
// 深度响应式(默认)
const deep = reactive({
level1: {
level2: {
level3: { value: 1 }
}
}
})
// 浅响应式(只代理第一层)
import { shallowReactive } from 'vue'
const shallow = shallowReactive({
level1: {
level2: {
level3: { value: 1 }
}
}
})
// shallow.level1 是响应式的
shallow.level1 = { level2: { level3: { value: 2 } } } // 触发更新
// shallow.level1.level2 不是响应式的
shallow.level1.level2.level3.value = 2 // 不触发更新
8.3 使用 readonly 避免意外修改
import { reactive, readonly } from 'vue'
const state = reactive({ count: 0 })
const readonlyState = readonly(state)
readonlyState.count = 1 // 警告:无法修改 readonly 对象
🎉 九、总结
本文深入讲解了 Vue3 响应式系统的实现原理:
- ✅ Proxy vs Object.defineProperty - Vue3 使用 Proxy 解决了 Vue2 的所有局限
- ✅ 核心实现 - reactive、ref、effect、computed 的完整实现
- ✅ 依赖收集和派发更新 - track 和 trigger 的机制
- ✅ 数据结构 - targetMap、depsMap、dep 的关系
- ✅ 性能优化 - shallowReactive、readonly 等优化手段
下一篇预告:
在下一篇文章中,我将讲解 Pinia 状态管理最佳实践,包括:
- Pinia 的核心概念和使用
- Store 的定义和组织
- 状态、Getters、Actions 的最佳实践
- Pinia vs Vuex 的对比
- 实战案例:用户认证、购物车
📦 获取完整源码
本文的完整响应式系统源码(含 TypeScript 类型、注释、测试用例),已经整理好了。
🔍 百度/Google 搜索:代码小库
在网站首页搜索「Vue3 响应式原理」即可找到:
- ✅ 完整的响应式系统实现(500+ 行代码)
- ✅ 包含 reactive、ref、computed、watch 等所有 API
- ✅ 完整的单元测试
- ✅ 视频讲解(手写响应式系统)
📢 关于作者
前端开发者,【代码小库】创建者,专注 Vue/React/Nuxt 实战教程。
🎯 我的公众号:代码小库
关注后回复关键词获取资源:
- 回复 vue3 → Vue3 实战源码包
- 回复 响应式 → 响应式系统完整源码
- 回复 进群 → 加入技术交流群
💡 本文是【Vue3 实战系列】第 3 篇,完整系列 30 篇,持续更新中…
📚 往期回顾:
- ✅ 第1篇:Vue3 + Vite 项目搭建完整指南
- ✅ 第2篇:Vue3 组合式API实战:从Options到Composition
🔜 下期预告:
- ⏳ 第4篇:Pinia 状态管理最佳实践【替代Vuex】
❤️ 觉得有帮助? 点赞👍 收藏⭐ 关注👀
💬 有疑问? 欢迎评论区留言,我会认真回复每一条!
#Vue3 #响应式原理 #Proxy #Reflect #源码解析 #代码小库
更多推荐



所有评论(0)