Vue 3 的响应式系统是其核心特性之一,它允许我们以声明式的方式处理数据变化。本文将深入分析 Vue 3 中 reactive.ts 文件的实现原理,帮助你全面理解 Vue 3 响应式系统的内部工作机制。
目录
- [Vue 3 Reactivity 系统完全指南:深入理解 reactive.ts](#Vue 3 Reactivity 系统完全指南:深入理解 reactive.ts "#vue-3-reactivity-%E7%B3%BB%E7%BB%9F%E5%AE%8C%E5%85%A8%E6%8C%87%E5%8D%97%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3-reactivets")
- 目录
- [1. 概述](#1. 概述 "#1-%E6%A6%82%E8%BF%B0")
- [2. 核心 API 详解](#2. 核心 API 详解 "#2-%E6%A0%B8%E5%BF%83-api-%E8%AF%A6%E8%A7%A3")
- [2.1 reactive](#2.1 reactive "#21-reactive")
- [2.2 shallowReactive](#2.2 shallowReactive "#22-shallowreactive")
- [2.3 readonly](#2.3 readonly "#23-readonly")
- [2.4 shallowReadonly](#2.4 shallowReadonly "#24-shallowreadonly")
- [3. 工具函数解析](#3. 工具函数解析 "#3-%E5%B7%A5%E5%85%B7%E5%87%BD%E6%95%B0%E8%A7%A3%E6%9E%90")
- [3.1 isReactive](#3.1 isReactive "#31-isreactive")
- [3.2 isReadonly](#3.2 isReadonly "#32-isreadonly")
- [3.3 isShallow](#3.3 isShallow "#33-isshallow")
- [3.4 isProxy](#3.4 isProxy "#34-isproxy")
- [3.5 toRaw](#3.5 toRaw "#35-toraw")
- [3.6 markRaw](#3.6 markRaw "#36-markraw")
- [3.7 toReactive 和 toReadonly](#3.7 toReactive 和 toReadonly "#37-toreactive-%E5%92%8C-toreadonly")
- [4. 内部实现机制](#4. 内部实现机制 "#4-%E5%86%85%E9%83%A8%E5%AE%9E%E7%8E%B0%E6%9C%BA%E5%88%B6")
- [4.1 createReactiveObject](#4.1 createReactiveObject "#41-createreactiveobject")
- [4.2 缓存机制](#4.2 缓存机制 "#42-%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6")
- [4.3 目标类型检测](#4.3 目标类型检测 "#43-%E7%9B%AE%E6%A0%87%E7%B1%BB%E5%9E%8B%E6%A3%80%E6%B5%8B")
- [5. TypeScript 高级特性应用](#5. TypeScript 高级特性应用 "#5-typescript-%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7%E5%BA%94%E7%94%A8")
- [5.1 泛型与条件类型](#5.1 泛型与条件类型 "#51-%E6%B3%9B%E5%9E%8B%E4%B8%8E%E6%9D%A1%E4%BB%B6%E7%B1%BB%E5%9E%8B")
- [5.2 映射类型](#5.2 映射类型 "#52-%E6%98%A0%E5%B0%84%E7%B1%BB%E5%9E%8B")
- [5.3 infer 关键字](#5.3 infer 关键字 "#53-infer-%E5%85%B3%E9%94%AE%E5%AD%97")
- [5.4 联合类型与交叉类型](#5.4 联合类型与交叉类型 "#54-%E8%81%94%E5%90%88%E7%B1%BB%E5%9E%8B%E4%B8%8E%E4%BA%A4%E5%8F%89%E7%B1%BB%E5%9E%8B")
- [5.5 unique symbol](#5.5 unique symbol "#55-unique-symbol")
- [5.6 函数重载](#5.6 函数重载 "#56-%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD")
- [6. 实际应用示例](#6. 实际应用示例 "#6-%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E7%A4%BA%E4%BE%8B")
- [7. 性能优化策略](#7. 性能优化策略 "#7-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E7%AD%96%E7%95%A5")
- [8. 总结](#8. 总结 "#8-%E6%80%BB%E7%BB%93")
1. 概述
reactive.ts 是 Vue 3 响应式系统的核心模块之一,负责创建和管理响应式对象。它通过 JavaScript 的 Proxy API 实现数据拦截,并结合 WeakMap 缓存机制来优化性能。
该模块提供了多种创建响应式数据的方法,满足不同场景的需求:
reactive
: 创建深层响应式对象shallowReactive
: 创建浅层响应式对象readonly
: 创建深层只读对象shallowReadonly
: 创建浅层只读对象
同时提供了一系列工具函数用于检测和操作响应式对象。
2. 核心 API 详解
2.1 reactive
reactive
是最常用的响应式 API,它创建一个深层响应式的代理对象。
typescript
export function reactive<T extends object>(target: T): Reactive<T>
特点:
- 深度转换:对象的所有嵌套属性都会被转换为响应式
- 自动解包 ref:如果属性是 ref,则会自动解包但仍保持响应性
- 不会代理非可扩展对象(如冻结对象)
使用示例:
javascript
import { reactive } from 'vue'
const state = reactive({
count: 0,
nested: {
foo: 'bar'
}
})
// 访问响应式属性
console.log(state.count) // 0
// 修改响应式属性
state.count++
state.nested.foo = 'baz'
2.2 shallowReactive
shallowReactive
创建一个浅层响应式对象,只有根级别的属性是响应式的。
typescript
export function shallowReactive<T extends object>(target: T): ShallowReactive<T>
特点:
- 只有根级别属性是响应式的
- 不会自动解包 ref 值
- 不会对嵌套对象进行递归响应式处理
使用示例:
javascript
import { shallowReactive } from 'vue'
const state = shallowReactive({
foo: 1,
nested: {
bar: 2
}
})
// 修改根级属性是响应式的
state.foo++
console.log(state.foo) // 2
// 嵌套对象不是响应式的
state.nested.bar++
// 不会触发响应式更新
2.3 readonly
readonly
创建一个只读代理对象,所有修改操作都会被拦截并警告。
typescript
export function readonly<T extends object>(target: T): DeepReadonly<UnwrapNestedRefs<T>>
特点:
- 深度只读:所有嵌套属性都是只读的
- 保持响应性:可以追踪变化并触发依赖更新
- 适用于需要防止意外修改的场景
使用示例:
javascript
import { reactive, readonly } from 'vue'
const original = reactive({ count: 0 })
const copy = readonly(original)
// 读取操作正常工作
console.log(copy.count) // 0
// 修改原始对象会触发依赖更新
original.count++
// 修改副本会发出警告
copy.count++ // warning!
2.4 shallowReadonly
shallowReadonly
创建一个浅层只读对象,只有根级别的属性是只读的。
typescript
export function shallowReadonly<T extends object>(target: T): Readonly<T>
特点:
- 只有根级别属性是只读的
- 不会自动解包 ref 值
- 嵌套对象可以被修改
使用示例:
javascript
import { shallowReadonly } from 'vue'
const state = shallowReadonly({
foo: 1,
nested: {
bar: 2
}
})
// 修改根级属性会被拦截
state.foo++ // 警告
// 嵌套对象可以被修改
state.nested.bar++ // 正常工作
3. 工具函数解析
3.1 isReactive
检查对象是否是由 reactive
或 shallowReactive
创建的代理。
typescript
export function isReactive(value: unknown): boolean
使用示例:
javascript
import { reactive, readonly, ref, shallowReactive } from 'vue'
console.log(isReactive(reactive({}))) // true
console.log(isReactive(readonly(reactive({}))) ) // true
console.log(isReactive(ref({}).value)) // true
console.log(isReactive(ref(true))) // false
console.log(isReactive(shallowReactive({}))) // true
3.2 isReadonly
检查对象是否是只读的。
typescript
export function isReadonly(value: unknown): boolean
使用示例:
javascript
import { reactive, readonly, shallowReadonly } from 'vue'
console.log(isReadonly(readonly({}))) // true
console.log(isReadonly(shallowReadonly({}))) // true
console.log(isReadonly(reactive({}))) // false
3.3 isShallow
检查对象是否是浅层的。
typescript
export function isShallow(value: unknown): boolean
3.4 isProxy
检查对象是否是由 reactive、readonly、shallowReactive 或 shallowReadonly 创建的代理。
typescript
export function isProxy(value: any): boolean
3.5 toRaw
返回 Vue 创建的代理对象的原始对象。
typescript
export function toRaw<T>(observed: T): T
使用示例:
javascript
import { reactive, toRaw } from 'vue'
const foo = {}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo) // true
3.6 markRaw
标记一个对象,使其永远不会被转换为代理。
typescript
export function markRaw<T extends object>(value: T): Raw<T>
使用示例:
javascript
import { reactive, markRaw, isReactive } from 'vue'
const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false
// 也适用于嵌套对象
const bar = reactive({ foo })
console.log(isReactive(bar.foo)) // false
3.7 toReactive 和 toReadonly
这两个辅助函数用于有条件地创建响应式或只读代理。
typescript
export const toReactive = <T extends unknown>(value: T): T =>
isObject(value) ? reactive(value) : value
export const toReadonly = <T extends unknown>(value: T): DeepReadonly<T> =>
isObject(value) ? readonly(value) : (value as DeepReadonly<T>)
4. 内部实现机制
4.1 createReactiveObject
所有公共 API 都通过 createReactiveObject
函数实现:
typescript
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>,
)
该函数处理了多种边界情况:
- 检查目标是否为对象
- 避免重复代理
- 处理只读和响应式的特殊情况
- 根据对象类型选择合适的处理器
4.2 缓存机制
通过 WeakMap 缓存已创建的代理对象,避免重复创建:
typescript
export const reactiveMap: WeakMap<Target, any> = new WeakMap<Target, any>()
export const shallowReactiveMap: WeakMap<Target, any> = new WeakMap<Target, any>()
export const readonlyMap: WeakMap<Target, any> = new WeakMap<Target, any>()
export const shallowReadonlyMap: WeakMap<Target, any> = new WeakMap<Target, any>()
4.3 目标类型检测
通过 TargetType
枚举和相关函数区分不同类型的对象:
typescript
enum TargetType {
INVALID = 0,
COMMON = 1,
COLLECTION = 2,
}
function targetTypeMap(rawType: string) {
switch (rawType) {
case 'Object':
case 'Array':
return TargetType.COMMON
case 'Map':
case 'Set':
case 'WeakMap':
case 'WeakSet':
return TargetType.COLLECTION
default:
return TargetType.INVALID
}
}
5. TypeScript 高级特性应用
5.1 泛型与条件类型
typescript
export type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRefSimple<T>
这个条件类型用于判断是否需要解包嵌套的 refs。
5.2 映射类型
typescript
export type DeepReadonly<T> = T extends Builtin
? T
: T extends Map<infer K, infer V>
? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
// ... 更多条件分支
: T extends {}
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: Readonly<T>
使用映射类型递归创建深度只读类型。
5.3 infer 关键字
在 DeepReadonly<T>
类型中使用 infer
提取泛型参数:
typescript
T extends Map<infer K, infer V>
? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
5.4 联合类型与交叉类型
typescript
type Primitive = string | number | boolean | bigint | symbol | undefined | null
export type Reactive<T> = UnwrapNestedRefs<T> &
(T extends readonly any[] ? ReactiveMarker : {})
5.5 unique symbol
使用 unique symbol 创建全局唯一的标识符:
typescript
declare const ReactiveMarkerSymbol: unique symbol
export interface ReactiveMarker {
[ReactiveMarkerSymbol]?: void
}
5.6 函数重载
typescript
export function reactive<T extends object>(target: T): Reactive<T>
export function reactive(target: object)
通过函数重载提供更精确的类型定义。
6. 实际应用示例
javascript
import { reactive, readonly, isReactive, toRaw } from 'vue'
// 创建响应式状态
const state = reactive({
users: [],
loading: false,
error: null
})
// 创建只读状态供组件使用
const stateReader = readonly(state)
// 在组件中使用
export default {
data() {
return {
state: stateReader
}
},
methods: {
async loadUsers() {
// 修改原始状态
state.loading = true
try {
const users = await fetchUsers()
state.users = users
} catch (error) {
state.error = error.message
} finally {
state.loading = false
}
}
},
computed: {
userCount() {
// 通过只读代理访问
return this.state.users.length
}
}
}
7. 性能优化策略
- 缓存机制:通过 WeakMap 避免重复创建代理对象
- 类型区分:为不同类型的对象(普通对象、集合)使用不同的处理器
- 跳过标记 :通过
markRaw
跳过不需要响应式的对象 - 浅层处理 :在不需要深度响应式时使用
shallowReactive
8. 总结
Vue 3 的 reactive.ts 文件通过巧妙地结合 Proxy API、WeakMap 缓存和 TypeScript 高级类型系统,实现了强大而灵活的响应式系统。它不仅提供了多种 API 满足不同场景需求,还通过各种优化策略保证了良好的性能。
理解这些实现细节有助于我们:
- 更好地使用 Vue 3 的响应式 API
- 在遇到问题时能够快速定位和解决
- 学习如何在项目中应用类似的模式
通过深入分析这个文件,我们可以看到 Vue 3 在设计上的精妙之处,以及如何通过 TypeScript 的类型系统提供既安全又灵活的 API。