本文档将逐行解析 Vue 3 中 ref.ts 文件的源码实现,深入理解 Vue 3 的响应式系统中 ref 的工作原理。
文件头部导入部分
typescript
import {
type IfAny,
hasChanged,
isArray,
isFunction,
isObject,
} from '@vue/shared'
import { Dep, getDepFromReactive } from './dep'
import {
type Builtin,
type ShallowReactiveMarker,
isProxy,
isReactive,
isReadonly,
isShallow,
toRaw,
toReactive,
} from './reactive'
import type { ComputedRef, WritableComputedRef } from './computed'
import { ReactiveFlags, TrackOpTypes, TriggerOpTypes } from './constants'
import { warn } from './warning'
导入部分引入了多个工具函数和类型定义:
- 从
@vue/shared
导入通用工具函数和类型 - 从 dep.ts 导入依赖追踪相关类
- 从 reactive.ts 导入响应式系统核心函数
- 从 computed.ts 导入计算属性相关类型
- 从 constants.ts 导入常量枚举
- 从 warning.ts 导入警告函数
typescript
declare const RefSymbol: unique symbol
export declare const RawSymbol: unique symbol
声明了两个唯一的 symbol 类型,用于类型系统中的标识符。RefSymbol 用于区分 Ref 对象,而 RawSymbol 在这里重新声明,可能用于标识原始值。
Ref 接口定义
typescript
export interface Ref<T = any, S = T> {
get value(): T
set value(_: S)
/**
* Type differentiator only.
* We need this to be in public d.ts but don't want it to show up in IDE
* autocomplete, so we use a private Symbol instead.
*/
[RefSymbol]: true
}
定义了 Ref 接口,它具有以下特征:
- 有一个 value 属性,具有 getter 和 setter
- 使用 RefSymbol 作为类型区分标识符,这样可以在运行时检查一个对象是否为 Ref
isRef 函数
typescript
/**
* Checks if a value is a ref object.
*
* @param r - The value to inspect.
* @see {@link https://vuejs.org/api/reactivity-utilities.html#isref}
*/
export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
export function isRef(r: any): r is Ref {
return r ? r[ReactiveFlags.IS_REF] === true : false
}
isRef 函数用于检查一个值是否为 Ref 对象。通过检查对象的 ReactiveFlags.IS_REF 属性是否为 true 来判断。
ref 函数
typescript
/**
* Takes an inner value and returns a reactive and mutable ref object, which
* has a single property `.value` that points to the inner value.
*
* @param value - The object to wrap in the ref.
* @see {@link https://vuejs.org/api/reactivity-core.html#ref}
*/
export function ref<T>(
value: T,
): [T] extends [Ref] ? IfAny<T, Ref<T>, T> : Ref<UnwrapRef<T>, UnwrapRef<T> | T>
export function ref<T = any>(): Ref<T | undefined>
export function ref(value?: unknown) {
return createRef(value, false)
}
ref 函数创建一个响应式的 ref 对象。函数重载支持两种情况:
- 传入一个值,创建对应的 ref
- 不传值,创建一个值为 undefined 的 ref
实际实现委托给 createRef 函数,第二个参数 false 表示创建的是深度响应式 ref。
ShallowRef 相关类型和函数
typescript
declare const ShallowRefMarker: unique symbol
export type ShallowRef<T = any, S = T> = Ref<T, S> & {
[ShallowRefMarker]?: true
}
声明了 ShallowRefMarker symbol 并定义了 ShallowRef 类型,它是 Ref 的扩展,添加了 ShallowRefMarker 标记。
typescript
/**
* Shallow version of {@link ref}.
*
* @example
* ```js
* const state = shallowRef({ count: 1 })
*
* // does NOT trigger change
* state.value.count = 2
*
* // does trigger change
* state.value = { count: 2 }
* ```
*
* @param value - The "inner value" for the shallow ref.
* @see {@link https://vuejs.org/api/reactivity-advanced.html#shallowref}
*/
export function shallowRef<T>(
value: T,
): Ref extends T
? T extends Ref
? IfAny<T, ShallowRef<T>, T>
: ShallowRef<T>
: ShallowRef<T>
export function shallowRef<T = any>(): ShallowRef<T | undefined>
export function shallowRef(value?: unknown) {
return createRef(value, true)
}
shallowRef 函数创建一个浅层响应式的 ref 对象。与 ref 不同,它只对 .value
的赋值操作是响应式的,而对值内部属性的修改不是响应式的。
createRef 函数
typescript
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
createRef 是创建 ref 的内部函数:
- 如果传入的值已经是 ref,则直接返回
- 否则创建一个新的 RefImpl 实例
RefImpl 类
typescript
/**
* @internal
*/
class RefImpl<T = any> {
_value: T
private _rawValue: T
dep: Dep = new Dep()
public readonly [ReactiveFlags.IS_REF] = true
public readonly [ReactiveFlags.IS_SHALLOW]: boolean = false
constructor(value: T, isShallow: boolean) {
this._rawValue = isShallow ? value : toRaw(value)
this._value = isShallow ? value : toReactive(value)
this[ReactiveFlags.IS_SHALLOW] = isShallow
}
RefImpl 是 Ref 的具体实现类:
- _value 存储当前值
- _rawValue 存储原始值
- dep 依赖收集器
- ReactiveFlags.IS_REF 标记这是 ref 对象
- ReactiveFlags.IS_SHALLOW 标记是否为浅层响应式
构造函数中根据是否为浅层响应式来决定如何处理传入的值。
typescript
get value() {
if (__DEV__) {
this.dep.track({
target: this,
type: TrackOpTypes.GET,
key: 'value',
})
} else {
this.dep.track()
}
return this._value
}
value 的 getter 方法:
- 在开发环境下,会跟踪详细的调试信息
- 在生产环境下,简化依赖追踪
- 返回 _value
typescript
set value(newValue) {
const oldValue = this._rawValue
const useDirectValue =
this[ReactiveFlags.IS_SHALLOW] ||
isShallow(newValue) ||
isReadonly(newValue)
newValue = useDirectValue ? newValue : toRaw(newValue)
if (hasChanged(newValue, oldValue)) {
this._rawValue = newValue
this._value = useDirectValue ? newValue : toReactive(newValue)
if (__DEV__) {
this.dep.trigger({
target: this,
type: TriggerOpTypes.SET,
key: 'value',
newValue,
oldValue,
})
} else {
this.dep.trigger()
}
}
}
}
value 的 setter 方法:
triggerRef 函数
typescript
/**
* Force trigger effects that depends on a shallow ref. This is typically used
* after making deep mutations to the inner value of a shallow ref.
*
* @example
* ```js
* const shallow = shallowRef({
* greet: 'Hello, world'
* })
*
* // Logs "Hello, world" once for the first run-through
* watchEffect(() => {
* console.log(shallow.value.greet)
* })
*
* // This won't trigger the effect because the ref is shallow
* shallow.value.greet = 'Hello, universe'
*
* // Logs "Hello, universe"
* triggerRef(shallow)
* ```
*
* @param ref - The ref whose tied effects shall be executed.
* @see {@link https://vuejs.org/api/reactivity-advanced.html#triggerref}
*/
export function triggerRef(ref: Ref): void {
// ref may be an instance of ObjectRefImpl
if ((ref as unknown as RefImpl).dep) {
if (__DEV__) {
;(ref as unknown as RefImpl).dep.trigger({
target: ref,
type: TriggerOpTypes.SET,
key: 'value',
newValue: (ref as unknown as RefImpl)._value,
})
} else {
;(ref as unknown as RefImpl).dep.trigger()
}
}
}
triggerRef 函数用于手动触发浅层 ref 的依赖更新,通常在修改了浅层 ref 内部属性后使用。
类型定义
typescript
export type MaybeRef<T = any> =
| T
| Ref<T>
| ShallowRef<T>
| WritableComputedRef<T>
export type MaybeRefOrGetter<T = any> = MaybeRef<T> | ComputedRef<T> | (() => T)
定义了两个常用的类型别名:
- MaybeRef 表示可能是 ref 也可能不是的值
- MaybeRefOrGetter 表示可能是 ref、只读计算属性或 getter 函数的值
unref 函数
typescript
/**
* Returns the inner value if the argument is a ref, otherwise return the
* argument itself. This is a sugar function for
* `val = isRef(val) ? val.value : val`.
*
* @example
* ```js
* function useFoo(x: number | Ref<number>) {
* const unwrapped = unref(x)
* // unwrapped is guaranteed to be number now
* }
* ```
*
* @param ref - Ref or plain value to be converted into the plain value.
* @see {@link https://vuejs.org/api/reactivity-utilities.html#unref}
*/
export function unref<T>(ref: MaybeRef<T> | ComputedRef<T>): T {
return isRef(ref) ? ref.value : ref
}
unref 函数是 isRef(val) ? val.value : val
的语法糖,用于获取 ref 的值或直接返回非 ref 值。
toValue 函数
typescript
/**
* Normalizes values / refs / getters to values.
* This is similar to {@link unref}, except that it also normalizes getters.
* If the argument is a getter, it will be invoked and its return value will
* be returned.
*
* @example
* ```js
* toValue(1) // 1
* toValue(ref(1)) // 1
* toValue(() => 1) // 1
* ```
*
* @param source - A getter, an existing ref, or a non-function value.
* @see {@link https://vuejs.org/api/reactivity-utilities.html#tovalue}
*/
export function toValue<T>(source: MaybeRefOrGetter<T>): T {
return isFunction(source) ? source() : unref(source)
}
toValue 函数扩展了 unref 的功能,除了处理 ref 外还能处理 getter 函数。
proxyRefs 函数
typescript
const shallowUnwrapHandlers: ProxyHandler<any> = {
get: (target, key, receiver) =>
key === ReactiveFlags.RAW
? target
: unref(Reflect.get(target, key, receiver)),
set: (target, key, value, receiver) => {
const oldValue = target[key]
if (isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
} else {
return Reflect.set(target, key, value, receiver)
}
},
}
定义了用于 proxyRefs 的代理处理器:
- get 操作:如果是 RAW 标志则返回目标对象,否则对获取的值进行 unref 操作
- set 操作:如果旧值是 ref 且新值不是 ref,则更新旧值 ref 的 value,否则直接设置属性
typescript
/**
* Returns a proxy for the given object that shallowly unwraps properties that
* are refs. If the object already is reactive, it's returned as-is. If not, a
* new reactive proxy is created.
*
* @param objectWithRefs - Either an already-reactive object or a simple object
* that contains refs.
*/
export function proxyRefs<T extends object>(
objectWithRefs: T,
): ShallowUnwrapRef<T> {
return isReactive(objectWithRefs)
? objectWithRefs
: new Proxy(objectWithRefs, shallowUnwrapHandlers)
}
proxyRefs 函数创建一个代理对象,该对象会自动解包属性中的 ref:
- 如果对象已经是响应式的,直接返回
- 否则创建一个使用 shallowUnwrapHandlers 处理器的代理
CustomRef 相关实现
typescript
export type CustomRefFactory<T> = (
track: () => void,
trigger: () => void,
) => {
get: () => T
set: (value: T) => void
}
定义了 CustomRefFactory 类型,这是一个工厂函数类型,接收 track 和 trigger 函数作为参数,返回包含 get 和 set 方法的对象。
typescript
class CustomRefImpl<T> {
public dep: Dep
private readonly _get: ReturnType<CustomRefFactory<T>>['get']
private readonly _set: ReturnType<CustomRefFactory<T>>['set']
public readonly [ReactiveFlags.IS_REF] = true
public _value: T = undefined!
constructor(factory: CustomRefFactory<T>) {
const dep = (this.dep = new Dep())
const { get, set } = factory(dep.track.bind(dep), dep.trigger.bind(dep))
this._get = get
this._set = set
}
get value() {
return (this._value = this._get())
}
set value(newVal) {
this._set(newVal)
}
}
CustomRefImpl 是自定义 ref 的实现:
- 通过工厂函数获取自定义的 get 和 set 方法
- get 方法执行时会调用自定义的 get 方法并更新 _value
- set 方法执行时会调用自定义的 set 方法
typescript
/**
* Creates a customized ref with explicit control over its dependency tracking
* and updates triggering.
*
* @param factory - The function that receives the `track` and `trigger` callbacks.
* @see {@link https://vuejs.org/api/reactivity-advanced.html#customref}
*/
export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
return new CustomRefImpl(factory) as any
}
customRef 函数用于创建自定义 ref,允许用户显式控制依赖追踪和更新触发。
toRefs 函数
typescript
export type ToRefs<T = any> = {
[K in keyof T]: ToRef<T[K]>
}
ToRefs 类型定义了将对象的所有属性转换为 ref 的类型。
typescript
/**
* Converts a reactive object to a plain object where each property of the
* resulting object is a ref pointing to the corresponding property of the
* original object. Each individual ref is created using {@link toRef}.
*
* @param object - Reactive object to be made into an object of linked refs.
* @see {@link https://vuejs.org/api/reactivity-utilities.html#torefs}
*/
export function toRefs<T extends object>(object: T): ToRefs<T> {
if (__DEV__ && !isProxy(object)) {
warn(`toRefs() expects a reactive object but received a plain one.`)
}
const ret: any = isArray(object) ? new Array(object.length) : {}
for (const key in object) {
ret[key] = propertyToRef(object, key)
}
return ret
}
toRefs 函数将响应式对象的所有属性转换为 ref:
- 开发环境下会检查对象是否为响应式对象
- 根据对象是否为数组创建对应的返回容器
- 遍历对象属性,使用 propertyToRef 将每个属性转换为 ref
ObjectRefImpl 类
typescript
class ObjectRefImpl<T extends object, K extends keyof T> {
public readonly [ReactiveFlags.IS_REF] = true
public _value: T[K] = undefined!
constructor(
private readonly _object: T,
private readonly _key: K,
private readonly _defaultValue?: T[K],
) {}
get value() {
const val = this._object[this._key]
return (this._value = val === undefined ? this._defaultValue! : val)
}
set value(newVal) {
this._object[this._key] = newVal
}
get dep(): Dep | undefined {
return getDepFromReactive(toRaw(this._object), this._key)
}
}
ObjectRefImpl 是属性 ref 的实现:
- 创建一个指向对象特定属性的 ref
- get 操作返回对象对应属性的值
- set 操作更新对象对应属性的值
- dep 从原始对象的响应式系统中获取
GetterRefImpl 类
typescript
class GetterRefImpl<T> {
public readonly [ReactiveFlags.IS_REF] = true
public readonly [ReactiveFlags.IS_READONLY] = true
public _value: T = undefined!
constructor(private readonly _getter: () => T) {}
get value() {
return (this._value = this._getter())
}
}
GetterRefImpl 是 getter ref 的实现:
- 创建一个只读的 ref,其值来自 getter 函数
- 只有 get 操作,没有 set 操作
toRef 函数
typescript
export type ToRef<T> = IfAny<T, Ref<T>, [T] extends [Ref] ? T : Ref<T>>
ToRef 类型定义了将值转换为 ref 的类型。
typescript
/**
* Used to normalize values / refs / getters into refs.
*
* @example
* ```js
* // returns existing refs as-is
* toRef(existingRef)
*
* // creates a ref that calls the getter on .value access
* toRef(() => props.foo)
*
* // creates normal refs from non-function values
* // equivalent to ref(1)
* toRef(1)
* ```
*
* Can also be used to create a ref for a property on a source reactive object.
* The created ref is synced with its source property: mutating the source
* property will update the ref, and vice-versa.
*
* @example
* ```js
* const state = reactive({
* foo: 1,
* bar: 2
* })
*
* const fooRef = toRef(state, 'foo')
*
* // mutating the ref updates the original
* fooRef.value++
* console.log(state.foo) // 2
*
* // mutating the original also updates the ref
* state.foo++
* console.log(fooRef.value) // 3
* ```
*
* @param source - A getter, an existing ref, a non-function value, or a
* reactive object to create a property ref from.
* @param [key] - (optional) Name of the property in the reactive object.
* @see {@link https://vuejs.org/api/reactivity-utilities.html#toref}
*/
export function toRef<T>(
value: T,
): T extends () => infer R
? Readonly<Ref<R>>
: T extends Ref
? T
: Ref<UnwrapRef<T>>
export function toRef<T extends object, K extends keyof T>(
object: T,
key: K,
): ToRef<T[K]>
export function toRef<T extends object, K extends keyof T>(
object: T,
key: K,
defaultValue: T[K],
): ToRef<Exclude<T[K], undefined>>
export function toRef(
source: Record<string, any> | MaybeRef,
key?: string,
defaultValue?: unknown,
): Ref {
if (isRef(source)) {
return source
} else if (isFunction(source)) {
return new GetterRefImpl(source) as any
} else if (isObject(source) && arguments.length > 1) {
return propertyToRef(source, key!, defaultValue)
} else {
return ref(source)
}
}
toRef 函数用于将值标准化为 ref:
- 如果是 ref,直接返回
- 如果是函数,创建 GetterRefImpl
- 如果是对象且提供了 key,使用 propertyToRef
- 否则创建普通 ref
typescript
function propertyToRef(
source: Record<string, any>,
key: string,
defaultValue?: unknown,
) {
const val = source[key]
return isRef(val)
? val
: (new ObjectRefImpl(source, key, defaultValue) as any)
}
propertyToRef 函数用于创建属性 ref:
- 如果属性值已经是 ref,直接返回
- 否则创建 ObjectRefImpl 实例
类型定义
typescript
/**
* This is a special exported interface for other packages to declare
* additional types that should bail out for ref unwrapping. For example
* \@vue/runtime-dom can declare it like so in its d.ts:
*
* ``` ts
* declare module '@vue/reactivity' {
* export interface RefUnwrapBailTypes {
* runtimeDOMBailTypes: Node | Window
* }
* }
* ```
*/
export interface RefUnwrapBailTypes {}
export type ShallowUnwrapRef<T> = {
[K in keyof T]: DistributeRef<T[K]>
}
type DistributeRef<T> = T extends Ref<infer V, unknown> ? V : T
定义了 RefUnwrapBailTypes 接口和相关类型,允许其他包声明不应解包的类型。
typescript
export type UnwrapRef<T> =
T extends ShallowRef<infer V, unknown>
? V
: T extends Ref<infer V, unknown>
? UnwrapRefSimple<V>
: UnwrapRefSimple<T>
UnwrapRef 类型用于解包 ref 类型,获取其内部值的类型。
typescript
export type UnwrapRefSimple<T> = T extends
| Builtin
| Ref
| RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
| { [RawSymbol]?: true }
? T
: T extends Map<infer K, infer V>
? Map<K, UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Map<any, any>>>
: T extends WeakMap<infer K, infer V>
? WeakMap<K, UnwrapRefSimple<V>> &
UnwrapRef<Omit<T, keyof WeakMap<any, any>>>
: T extends Set<infer V>
? Set<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof Set<any>>>
: T extends WeakSet<infer V>
? WeakSet<UnwrapRefSimple<V>> & UnwrapRef<Omit<T, keyof WeakSet<any>>>
: T extends ReadonlyArray<any>
? { [K in keyof T]: UnwrapRefSimple<T[K]> }
: T extends object & { [ShallowReactiveMarker]?: never }
? {
[P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>
}
: T
UnwrapRefSimple 类型递归解包复杂类型的 ref,处理各种内置对象类型和自定义对象类型。
TypeScript 语法解析
这个文件中使用了许多高级 TypeScript 语法和特性,下面是详细解析:
1. 泛型 (Generics)
typescript
export interface Ref<T = any, S = T> {
get value(): T
set value(_: S)
// ...
}
这里定义了 Ref 接口,使用了泛型参数 T 和 S,其中 S 默认值为 T。这允许 Ref 对象的 getter 和 setter 可以有不同的类型。
2. 条件类型 (Conditional Types)
typescript
export function ref<T>(
value: T,
): [T] extends [Ref] ? IfAny<T, Ref<T>, T> : Ref<UnwrapRef<T>, UnwrapRef<T> | T>
这个函数签名使用了条件类型,根据 T 是否继承自 Ref 来决定返回类型。如果 T 是 Ref 类型,则根据 IfAny 工具类型决定返回 Ref 还是 T;否则返回 Ref<UnwrapRef, UnwrapRef | T>。
3. 映射类型 (Mapped Types)
typescript
export type ToRefs<T = any> = {
[K in keyof T]: ToRef<T[K]>
}
这里使用了映射类型,将对象 T 的每个属性 K 映射为 ToRef<T[K]> 类型。
4. infer 关键字
typescript
T extends () => infer R
? Readonly<Ref<R>>
: T extends Ref
? T
: Ref<UnwrapRef<T>>
在条件类型中使用 infer 关键字可以从函数类型中推断出返回值类型 R。
5. 模板字面量类型 (Template Literal Types)
虽然在这个文件中没有直接使用,但 Vue 项目中广泛使用了这种类型,用于创建更精确的类型。
6. 类型守卫 (Type Guards)
typescript
export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
使用 r is Ref<T>
语法创建类型守卫函数,这在 TypeScript 中用于运行时类型检查。
7. 索引类型 (Indexed Types)
typescript
class ObjectRefImpl<T extends object, K extends keyof T> {
// ...
get value() {
const val = this._object[this._key] // 使用索引类型访问属性
return (this._value = val === undefined ? this._defaultValue! : val)
}
}
通过 keyof T
和 T[K]
语法实现对对象属性的类型安全访问。
8. 装饰器和访问修饰符
typescript
class RefImpl<T = any> {
_value: T
private _rawValue: T
dep: Dep = new Dep()
public readonly [ReactiveFlags.IS_REF] = true
public readonly [ReactiveFlags.IS_SHALLOW]: boolean = false
// ...
}
使用了 public、private 和 readonly 等访问修饰符和属性初始化器。
9. 类型断言
typescript
return new CustomRefImpl(factory) as any
在适当的时候使用类型断言来处理复杂的类型关系。
10. 联合类型和交叉类型
typescript
export type MaybeRef<T = any> =
| T
| Ref<T>
| ShallowRef<T>
| WritableComputedRef<T>
使用联合类型表示一个值可能是多种类型中的一种。
11. 实用工具类型
文件中使用了多种 TypeScript 内置工具类型,如:
Partial<T>
- 将 T 中的所有属性变为可选Readonly<T>
- 将 T 中的所有属性变为只读Pick<T, K>
- 从 T 中选择一组属性 KOmit<T, K>
- 从 T 中排除属性 K
12. 唯一符号 (unique symbol)
typescript
declare const RefSymbol: unique symbol
使用 unique symbol 创建全局唯一的符号,用于类型系统中的唯一标识。
13. 模块增强 (Module Augmentation)
typescript
/**
* This is a special exported interface for other packages to declare
* additional types that should bail out for ref unwrapping.
*/
export interface RefUnwrapBailTypes {}
定义了可以被其他模块扩展的接口,这是一种模块增强的模式。
14. 函数重载
typescript
export function shallowRef<T>(
value: T,
): Ref extends T
? T extends Ref
? IfAny<T, ShallowRef<T>, T>
: ShallowRef<T>
: ShallowRef<T>
export function shallowRef<T = any>(): ShallowRef<T | undefined>
export function shallowRef(value?: unknown) {
return createRef(value, true)
}
通过函数重载提供多种调用方式,增强函数的类型安全性。
总结
这个文件实现了 Vue 3 响应式系统中的 ref 相关功能,包括:
- 基础的 ref 和 shallowRef 创建函数
- isRef、unref、toRef、toRefs 等工具函数
- triggerRef 和 customRef 高级功能
- proxyRefs 和 toValue 便利函数
- 复杂的类型系统支持 ref 的类型推导和解包