vue3源码之reactive.ts

reactive()

reactive() 返回一个对象的响应式代理。是 vue 响应式 api 之一。

  • 类型
1
function reactive<T extends object>(target: T): UnwrapNestedRefs<T>

响应式转换是“深层”的:它会影响到所有嵌套的属性。

若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,请使用 shallowReactive() 作替代。

一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。值得注意的是,当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包。

1
2
3
4
5
6
7
8
9
10
11
12
13
const age = ref(18)

const ageList = reactive([age])
const personal = reactive({
age,
})

const age1 = ageList[0]
personal.age++
const age2 = personal.age

console.log('age1', age1) // RefImpl{}
console.log('age2', age2) // 19

响应式示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { reactive } from 'vue'

const obj = reactive({
nested: { count: 0 },
arr: ['foo', 'bar'],
})

watchEffect(() => {
console.log(obj.nested.count)
})

watchEffect(() => {
console.log(obj.arr.length)
})

obj.nested.count = 1000
obj.arr.push('baz')
// 0 obj.nested.count 初始值
// 2 obj.arr.length 初始值
// 1000 obj.nested.count 变化后
// 3 obj.arr.length 变化后

reactive 源码实现

reactive()

reactive() 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
// packages/reactivity/src/reactive.ts
export function reactive(target: object) {
if (isReadonly(target)) {
return target
}
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap,
)
}

通过 isReadonly 函数判断是直接返回 target 还是通过 createReactiveObject 函数创建一个响应式代理。


isReadonly()

1
2
3
if (isReadonly(target)) {
return target
}

判断 target 是否为只读对象,即 target 的 ReactiveFlags.IS_READONLY(__v_isReadonly) 属性值为 true,ReactiveFlags 取值相关逻辑位于 /packages/reactivity/src/baseHandlers.ts 的 BaseReactiveHandler.get 方法中。

应用示例

1
2
3
4
5
6
7
8
9
const ageList = readonly({
age: 18,
})
const ageList2 = reactive(ageList)
const ageList3 = shallowReadonly(ageList)

console.log(ageList === ageList2) // true
console.log(ageList2 === ageList3) // true
console.log(ageList === ageList3) // true

ReactiveFlags.IS_READONLY

具有 ReactiveFlags.IS_READONLY 属性的只读对象被其它响应式api包裹时,会直接返回原对象。


1
2
3
4
5
6
7
8
// packages/reactivity/src/reactive.ts
createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap,
)

通过 createReactiveObject 函数创建一个响应式对象。

  • target: 目标对象,即要创建响应式代理的对象。
  • isReadonly: 创建的代理对象是否为 readonly 类型,默认值为 false。
  • mutableHandlers: Object/Array 的 ProxyHandler 对象。
  • mutableCollectionHandlers: Map/Set/WeakMap/WeakSet的 ProxyHandler 对象。
  • reactiveMap: 已创建响应式代理的WeakMap,键为target,值为响应式代理。

createReactiveObject()

createReactiveObject源码

1
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
44
45
// packages/reactivity/src/reactive.ts
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>,
) {
if (!isObject(target)) {
if (__DEV__) {
warn(
`value cannot be made ${isReadonly ? 'readonly' : 'reactive'}: ${String(
target,
)}`,
)
}
return target
}

if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// only specific value types can be observed.
if (target[ReactiveFlags.SKIP] || !Object.isExtensible(target)) {
return target
}
// target already has corresponding Proxy
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
const targetType = targetTypeMap(toRawType(target))
if (targetType === TargetType.INVALID) {
return target
}
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,
)
proxyMap.set(target, proxy)
return proxy
}

非 Object 处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// packages/shared/src/general.ts
export const isObject = (val: unknown): val is Record<any, any> =>
val !== null && typeof val === 'object'

// packages/reactivity/src/reactive.ts
if (!isObject(target)) {
if (__DEV__) {
warn(
`value cannot be made ${isReadonly ? 'readonly' : 'reactive'}: ${String(
target,
)}`,
)
}
return target
}

通过 isObject() 函数判断 target 是否为对象,因为 new Proxy 只能代理对象,所以如果不是对象,则直接返回 target。如果是 dev 环境,通过 warn() 函数输出警告信息。


readonly 类型处理

1
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
// packages/reactivity/src/baseHandlers.ts
class BaseReactiveHandler implements ProxyHandler<Target> {
get(target: Target, key: string | symbol, receiver: object): any {
if (key === ReactiveFlags.RAW) {
if (
receiver ===
(isReadonly
? isShallow
? shallowReadonlyMap
: readonlyMap
: isShallow
? shallowReactiveMap
: reactiveMap
).get(target) ||
// receiver is not the reactive proxy, but has the same prototype
// this means the receiver is a user proxy of the reactive proxy
Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)
) {
return target
}
// early return undefined
return
}
}
}

// packages/reactivity/src/constants.ts
export enum ReactiveFlags {
IS_REACTIVE = '__v_isReactive',
RAW = '__v_raw',
}

// packages/reactivity/src/reactive.ts
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}

target[ReactiveFlags.RAW] 判断对象是否已经是代理对象,如果是代理对象,且重复创建,则直接返回原 target。

情况一

如果当前要创建的不是 readonly 类型代理,!(isReadonly && target[ReactiveFlags.IS_REACTIVE]) 中的 isReadonly 恒为 false,所以 !(false && ...) 恒为 true。即对一个已经是代理对象,且不是创建 readonly 类型的代理对象,返回自身。

1
2
3
4
5
6
7
8
9
10
const original = { foo: 1 }
const proxy = reactive(original)
const proxy2 = reactive(proxy)
const proxy3 = reactive(proxy)

// 命中条件:proxy[RAW] 存在 且 isReadonly 为 false, !(fasle && ...) 恒为 true
// 执行 return target,即直接返回 proxy,而不是再次包装
console.log(proxy === proxy2) // true
console.log(proxy2 === proxy3) // true
console.log(proxy === proxy3) // true

情况二

如果当前要创建的是 readonly 类型代理对象,此时判断 target[ReactiveFlags.IS_REACTIVE] 的值,

1
2
3
4
5
6
7
8
9
// packages/reactivity/src/baseHandlers.ts
class BaseReactiveHandler implements ProxyHandler<Target> {
get(target: Target, key: string | symbol, receiver: object): any {
const isReadonly = this._isReadonly
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly
}
}
}
方法 isReadonly ReactiveFlags.IS_REACTIVE
reactive false true
shallowReactive false true
readonly true false
shallowReadonly true false

由以上逻辑可知 ReactiveFlags.IS_REACTIVE === !isReadonly。readonly 类型代理对象的 ReactiveFlags.IS_REACTIVE 为 false,非 readonly 类型代理对象的 ReactiveFlags.IS_REACTIVE 为 true。

即如果要创建的是 readonly 类型代理对象,且 target 已经是 readonly 类型代理对象,则直接返回 target。

1
2
3
4
5
6
7
8
9
10
const original = { foo: 1 }
const proxy = readonly(original)
const proxy2 = shallowReadonly(proxy)

// 命中条件:
// - proxy[RAW] 存在
// - isReadonly 为 true
// - 不为 reactive 对象
// !(true && false) = true,即直接返回 target,而不是再次包装
console.log(proxy === proxy2) // true


跳过标记/不可扩展对象处理

1
2
3
4
5
6
7
8
9
// packages/reactivity/src/constants.ts
export enum ReactiveFlags {
SKIP = '__v_skip',
}

// packages/reactivity/src/reactive.ts
if (target[ReactiveFlags.SKIP] || !Object.isExtensible(target)) {
return target
}
  • ReactiveFlags.SKIP(__v_skip) 是 Vue 内部使用的一个特殊属性,它的核心作用是作为一个“跳过标记”,告诉 Vue 的响应式系统不要将这个对象转换为 Proxy 代理,从而让它保持原始状态,不被响应式化。
  • Object.isExtensible(obj) 是 JavaScript 用于‌判断对象是否允许添加新属性‌的静态方法。

如果 target 已被标记为 “跳过标记” 或者 不允许添加新属性,直接返回 target。

1
2
3
4
5
6
7
8
9
10
11
12
const original = { foo: 1 }
// 通过 markRaw 创建标记为 ReactiveFlags.SKIP 的代理对象
const original2 = markRaw({ foo: 1 })

// 通过 Object.freeze(original) 创建一个 Object.isExtensible 为 true 的对象
const obj = reactive(Object.freeze(original))
const obj2 = reactive(original2)

// 命中条件:!Object.isExtensible(obj) = true,未执行 reactive()
console.log(isReactive(obj)) // false
// 命中条件:ReactiveFlags.SKIP = true,未执行 reactive()
console.log(isReactive(obj2)) // false


target 重复创建处理

1
2
3
4
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}

proxyMap(一个WeakMap)会缓存已经生成的 Proxy 代理对象。判断 target 是否已经存在对应的 Proxy 代理。

如果 target 已经有对应的 Proxy 代理,直接返回该代理。

1
2
3
4
5
6
const raw = { name: 'Vue' }

const proxy1 = reactive(raw) // 首次调用,生成 Proxy,存入 proxyMap
const proxy2 = reactive(raw) // 再次调用,从 proxyMap 中找到并返回

console.log(proxy1 === proxy2) // 输出 true

existingProxy


target 合法性校验

1
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
// packages/reactivity/src/general.ts
export const objectToString: typeof Object.prototype.toString =
Object.prototype.toString
export const toTypeString = (value: unknown): string =>
objectToString.call(value)

export const toRawType = (value: unknown): string => {
// extract "RawType" from strings like "[object RawType]"
return toTypeString(value).slice(8, -1)
}

// packages/reactivity/src/reactive.ts

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
}
}

const targetType = targetTypeMap(toRawType(target))
if (targetType === TargetType.INVALID) {
return target
}

JavaScript 的 Proxy 只能代理对象(Object),无法代理基础类型。通过 Object.prototype.toString 方法获取 target 的数据类型,如果 target 不是 Object/Array/Map/Set/WeakMap/WeakSet 中的一种,直接返回 target。

1
2
3
4
5
6
const proxy1 = reactive(new Date())
const proxy2 = reactive(document.body)

// 不是有效的TargetType,直接返回 target
console.log(isReactive(proxy1)) // false
console.log(isReactive(proxy2)) // false


创建 Proxy 代理对象

1
2
3
4
5
6
7
// packages/reactivity/src/reactive.ts
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,
)
proxyMap.set(target, proxy)
return proxy

Proxy 对象允许你为另一个对象创建代理,该代理能够拦截并重新定义该对象的基本操作。

Proxy 需提供两个参数:

  • target: 需要代理的原始对象
  • handle: 定义哪些操作将被拦截以及如何重定义拦截操作的对象

根据 target 的数据类型选择不同的处理函数。如果 target 是 Map/Set/WeakMap/WeakSet 类型,使用 mutableCollectionHandlers 处理器对象,如果 target 是 Object/Array 类型,使用 mutableHandlers 处理器对象。

同时将 target 和 它相应的 proxy 对象存入 proxyMap 中。

最后返回 proxy 对象。

shallowReactive() 源码

1
2
3
4
5
6
7
8
9
10
11
12
// packages/reactivity/src/reactive.ts
export function shallowReactive<T extends object>(
target: T,
): ShallowReactive<T> {
return createReactiveObject(
target,
false,
shallowReactiveHandlers,
shallowCollectionHandlers,
shallowReactiveMap,
)
}

readonly() 源码

1
2
3
4
5
6
7
8
9
10
11
export function readonly<T extends object>(
target: T,
): DeepReadonly<UnwrapNestedRefs<T>> {
return createReactiveObject(
target,
true,
readonlyHandlers,
readonlyCollectionHandlers,
readonlyMap,
)
}

shallowReadonly() 源码

1
2
3
4
5
6
7
8
9
export function shallowReadonly<T extends object>(target: T): Readonly<T> {
return createReactiveObject(
target,
true,
shallowReadonlyHandlers,
shallowReadonlyCollectionHandlers,
shallowReadonlyMap,
)
}

注意事项

不能替换整个对象

由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:

1
2
3
4
5
6
7

let state = reactive({ count: 0 })

// 上面的 ({ count: 0 }) 引用将不再被追踪
// (响应性连接已丢失!)
state = reactive({ count: 1 })

对解构操作不友好

当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:

1
2
3
4
5
6
7
8
9
10
11
12
13

const state = reactive({ count: 0 })

// 当解构时,count 已经与 state.count 断开连接
let { count } = state
// 不会影响原始的 state
count++

// 该函数接收到的是一个普通的数字
// 并且无法追踪 state.count 的变化
// 我们必须传入整个对象以保持响应性
callSomeFunction(state.count)

原始对象不相等

reactive() 返回的是一个原始对象的 Proxy,它和原始对象是不相等的:

1
2
3
4
5
6
7

const raw = {}
const proxy = reactive(raw)

// 代理对象和原始对象不是全等的
console.log(proxy === raw) // false

isReactive() 源码

1
2
3
4
5
6
export function isReactive(value: unknown): boolean {
if (isReadonly(value)) {
return isReactive((value as Target)[ReactiveFlags.RAW])
}
return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
}

检查一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。

  • 如果 value 是只读对象,递归检查其原始对象。
  • 如果 value 对象的 ReactiveFlags.IS_REACTIVE 属性值为 true,则 value 是由 reactive() 或 shallowReactive() 创建的代理。
  • 否则不是。

使用示例

1
2
3
4
5
6
7
8
9
import { reactive, readonly, isReactive } from 'vue'

const proxy = reactive({
age: 18,
})
const proxy2 = readonly(proxy)

console.log(isReactive(proxy)) // true
console.log(isReactive(proxy2)) // true

isReadonly() 源码

1
2
3
4
5
6
7
8
9
10
11
// packages/reactivity/src/constants.ts
export enum ReactiveFlags {
...
IS_READONLY = '__v_isReadonly',
...
}

// packages/reactivity/src/reactive.ts
export function isReadonly(value: unknown): boolean {
return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
}

检查传入的值是否为只读对象,通过 readonly() 和 shallowReadonly() 创建的代理都是只读的。

获取 value 的 ReactiveFlags.IS_READONLY 属性值

  • 如果 value 对象的 ReactiveFlags.IS_READONLY 属性值为 true,则 value 是只读对象。
  • 否则不是。

使用示例

1
2
3
4
5
6
7
const proxy = reactive({
age: 18,
})
const proxy2 = readonly(proxy)

console.log(isReadonly(proxy)) // false
console.log(isReadonly(proxy2)) // true

isShallow() 源码

1
2
3
export function isShallow(value: unknown): boolean {
return !!(value && (value as Target)[ReactiveFlags.IS_SHALLOW])
}

检查一个对象是否是由 shallowRef()、shallowReactive() 或 shallowReadonly() 创建的代理。

获取 value 的 ReactiveFlags.IS_SHALLOW 属性值

  • 如果属性值为 true,则 value 是 shallow 类型代理对象。
  • 否则不是。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const proxy = ref({ age: 18 })
const proxy2 = shallowRef({ age: 18 })
const proxy3 = reactive({ age: 18 })
const proxy4 = shallowReactive({ age: 18 })
const proxy5 = readonly({ age: 18 })
const proxy6 = shallowReadonly({ age: 18 })

;[proxy, proxy2, proxy3, proxy4, proxy5, proxy6].forEach((item) => {
console.log(isShallow(item))
})

// false
// true
// false
// true
// false
// true

isProxy() 源码

1
2
3
export function isProxy(value: any): boolean {
return value ? !!value[ReactiveFlags.RAW] : false
}

检查一个对象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 创建的代理。

获取 value 的 ReactiveFlags.RAW 属性值

  • 如果属性值为 true,则 value 是由 reactive()/readonly()/shallowReactive()/shallowReadonly() 创建的代理。
  • 否则不是。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const proxy = ref({ age: 18 })
const proxy2 = shallowRef({ age: 18 })
const proxy3 = reactive({ age: 18 })
const proxy4 = shallowReactive({ age: 18 })
const proxy5 = readonly({ age: 18 })
const proxy6 = shallowReadonly({ age: 18 })

;[proxy, proxy2, proxy3, proxy4, proxy5, proxy6].forEach((item) => {
console.log(isProxy(item))
})

// false
// false
// true
// true
// true
// true

toRaw() 源码

1
2
3
4
export function toRaw<T>(observed: T): T {
const raw = observed && (observed as Target)[ReactiveFlags.RAW]
return raw ? toRaw(raw) : observed
}

toRaw() 可以返回由 reactive()、readonly()、shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象。

通过递归调用 toRaw() 函数,获取 observed 对象的 ReactiveFlags.RAW 属性值。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
const proxy = reactive({ age: 18 })
const proxy2 = shallowReactive({ age: 18 })
const proxy3 = readonly({ age: 18 })
const proxy4 = shallowReadonly({ age: 18 })

;[proxy, proxy2, proxy3, proxy4].forEach((item) => {
console.log(toRaw(item))
})

// { age: 18 }
// { age: 18 }
// { age: 18 }
// { age: 18 }

markRaw() 源码

1
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
// packages/reactivity/src/general.ts
const hasOwnProperty = Object.prototype.hasOwnProperty
export const hasOwn = (
val: object,
key: string | symbol,
): key is keyof typeof val => hasOwnProperty.call(val, key)

export const def = (
obj: object,
key: string | symbol,
value: any,
writable = false,
): void => {
Object.defineProperty(obj, key, {
configurable: true,
enumerable: false,
writable,
value,
})
}

// packages/reactivity/src/reactive.ts
export function markRaw<T extends object>(value: T): Raw<T> {
if (!hasOwn(value, ReactiveFlags.SKIP) && Object.isExtensible(value)) {
def(value, ReactiveFlags.SKIP, true)
}
return value
}

将一个对象标记为不可被转为代理。返回该对象本身。

通过 hasOwn 函数判断 value 自身是否有 ReactiveFlags.SKIP 属性

  • 如果自身没有 ReactiveFlags.SKIP 属性,且对象可扩展,则调用 def() 定义 value 对象的 ReactiveFlags.SKIP 属性为 true。
  • 否则返回原值 value

使用示例

1
2
3
4
const proxy = markRaw({ age: 18 })
const proxy2 = reactive(proxy)

const descriptor = Object.getOwnPropertyDescriptors(proxy)

toReactive() 源码

1
2
3
4
5
6
// packages/reactivity/src/general.ts
export const isObject = (val: unknown): val is Record<any, any> =>
val !== null && typeof val === 'object'

export const toReactive = <T extends unknown>(value: T): T =>
isObject(value) ? reactive(value) : value

toReactive() 传入一个对象,返回一个对象的响应式代理。

通过 isObject 函数判断 value 是否为对象类型

  • 如果是对象类型,则调用 reactive() 返回对象的响应式代理。
  • 否则返回原值 value

使用示例

1
2
3
4
5
import { toReadonly } from '@vue/reactivity'

const proxy = toReadonly({
age: 18,
})

toReadonly() 源码

1
2
3
4
5
6
7
// packages/reactivity/src/general.ts
export const isObject = (val: unknown): val is Record<any, any> =>
val !== null && typeof val === 'object'

// packages/reactivity/src/reactive.ts
export const toReadonly = <T extends unknown>(value: T): DeepReadonly<T> =>
isObject(value) ? readonly(value) : (value as DeepReadonly<T>)

检查传入的值是否为只读对象。只读对象的属性可以更改,但它们不能通过传入的对象直接赋值。

isObject 函数判断 value 是否为对象类型

  • 如果是对象类型,则调用 readonly() 返回 readonly 类型代理对象。
  • 否则返回原值 value

使用示例

1
2
3
4
5
import { toReadonly } from '@vue/reactivity'

const proxy = toReadonly({
age: 18,
})


vue3源码之reactive.ts
https://www.my-web.cn/vue/vue3源码之reactive/
发布于
2026年8月17日
许可协议