在 Vue3 中,因为 reactive 创建的响应式对象是通过 Proxy 来实现的,所以传入数据只能是引用类型
,而基础类型
无法实现,所以 ref 对象
是对 reactive 不支持的数据的一个补充,将基础类型包装成 Ref 对象从而实现响应式
辅助函数
isRef
TS
// packages/reactivity/src/ref.ts
export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
export function isRef(r: any): r is Ref { // is 是类型谓语作为运行时的类型检查
// 判断是否为Ref类型
return !!(r && r.__v_isRef === true) // !! 表示将类型转成布尔类型
}
shallowRef
- 浅层响应式:表示只有对象本身被响应式跟踪,如果对象的属性值是对象,则那些属性不会被响应式跟踪
- 深层响应式:表示对象及其嵌套属性都会被响应式跟踪
TS
// packages/reactivity/src/ref.ts
export function shallowRef<T = any>(): ShallowRef<T | undefined>
export function shallowRef(value?: unknown) {
return createRef(value, true) // 表示将对象标记为浅层响应式
}
toRaw
TS
// packages/reactivity/src/ref.ts
// 获取经过响应式处理后的原始值
export function toRaw<T>(observed: T): T {
const raw = observed && (observed as Target)[ReactiveFlags.RAW] // 被响应式处理后的对象会被标记为ReactiveFlags.RAW,可以存储原始值
return raw ? toRaw(raw) : observed // 递归调用toRaw函数,获取更深层次的原始值
}
createRef
TS
// packages/reactivity/src/ref.ts
export function ref(value?: unknown) {
return createRef(value, false) // 默认将对象标记为深层响应式
}
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
// 如果已经是Ref类型的值,直接返回
return rawValue
}
return new RefImpl(rawValue, shallow) // 创建实例
}
RefImpl
TS
// packages/reactivity/src/ref.ts
class RefImpl<T> {
constructor(
value: T,
public readonly __v_isShallow: boolean, // 是否使用浅层响应式
) {
this._rawValue = __v_isShallow ? value : toRaw(value) // 表示原始的值,即未处理的值
this._value = __v_isShallow ? value : toReactive(value) // 表示经响应式处理后的值
}
get value() {
trackRefValue(this) // 收集依赖
return this._value
}
set value(newVal) {
// useDirectValue用来判断是否直接使用新值
// - this.__v_isShallow:表示当前引用是否为浅层响应式
// - isShallow:表示新值是否为浅层响应式
// - isReadonly:表示新值是否为只读的
const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
newVal = useDirectValue ? newVal : toRaw(newVal) // useDirectValue为false时可能新值已经是响应式对象,需要转成原始类型
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = useDirectValue ? newVal : toReactive(newVal)
triggerRefValue(this, DirtyLevels.Dirty, newVal) // 触发依赖
}
}
}
trackRefValue
TS
// packages/reactivity/src/ref.ts
export function trackRefValue(ref: RefBase<any>) {
if (shouldTrack && activeEffect) {
// 只有在需要时且处于激活状态才追踪引用的值
// - 如果定义了Ref变量,但是没有任何地方使用到,此时就不需要收集依赖
// - 对于一些改变数组长度的方法中,就不需要收集依赖,防止造成无限循环
ref = toRaw(ref) // 确保追踪的是原始值而不是经过响应式处理后的值
trackEffect(
activeEffect,
// 简化写法,等价写法见如下
(ref.dep ??= createDep(
() => (ref.dep = undefined),
ref instanceof ComputedRefImpl ? ref : undefined,
)),
__DEV__
? {
target: ref,
type: TrackOpTypes.GET,
key: 'value',
}
: void 0,
)
}
}
TS
// 简化写法
(ref.dep ??= createDep(
() => (ref.dep = undefined),
ref instanceof ComputedRefImpl ? ref : undefined,
)),
// 等价写法
if (ref.dep === undefined || ref.dep === null) {
ref.dep = createDep(
() => (ref.dep = undefined),
ref instanceof ComputedRefImpl ? ref : undefined,
);
}
triggerRefValue
TS
// packages/reactivity/src/constants.ts
export enum DirtyLevels {
NotDirty = 0, // 未脏化:表示值没有发生变化
QueryingDirty = 1, // 查询脏化:表示引用在进行查询操作,通常处理一些异步操作
MaybeDirty_ComputedSideEffect = 2, // 可能脏化-计算副作用:由计算引用的副作用触发
MaybeDirty = 3, // 可能脏化:原因不明确
Dirty = 4, // 脏化:需要通知依赖于该引用的其他地方进行相应的更新操作
}
TS
// packages/reactivity/src/ref.ts
export function triggerRefValue(
ref: RefBase<any>,
dirtyLevel: DirtyLevels = DirtyLevels.Dirty, // 表示引用的脏状态,默认处于脏状态需要触发更新
newVal?: any,
) {
ref = toRaw(ref)
const dep = ref.dep
if (dep) {
triggerEffects(
dep,
dirtyLevel,
__DEV__
? {
target: ref,
type: TriggerOpTypes.SET,
key: 'value',
newValue: newVal,
}
: void 0,
)
}
}
类型嵌套
理想状态是直接通过 .value
获取值,而不是 .value.value.value....
的形式,目的是让ref(ref(ref('123')))
能够顺利推导出 ref 是 string 类型
UnwrapRef 类型
单层解包
运用 extends
和三元表达式
对 ref 函数的返回值进行条件判断
,如果 T 是 Ref 类型则直接返回,否则需要将 T 包装成 Ref 类型再返回
TS
type UnwrapRef<T> = T extends Ref<infer R> ? R : T
// 若 infer R 中的 R 还是 Ref 类型?
递归解包
用索引签名
的形式,增加能够停止递归的条件,对 Ref 类型进行反解
TS
type UnwrapRef<T> = {
ref: T extends Ref<infer R> ? R : T
other: T
}[T extends Ref ? 'ref' : 'other']
对象解包
Ts
type UnwarpRef<T> = {
ref: T extends Ref<infer R> ? R : T
// 注意这里
object: { [K in keyof T]: UnwarpRef<T[K]> }
other: T
}
[T extends Ref
? 'ref' : T extends object
? 'object'
: 'other'
]
完整源码
- 参数是简单类型直接返回
- 参数是 Ref ,用 infer v 获取参数类型返回v
- 参数是 object,遍历 object 将属性反解成简单类型
- 参数是 Array,遍历 Array 保留数组元素中的类型
- 参数是 Map、WeakMap、Set、WeakSet,返回对应类型,其中值被递归反解
TS
// packages/reactivity/src/ref.ts
export type UnwrapRef<T> =
T extends ShallowRef<infer V>
? V
: T extends Ref<infer V>
? UnwrapRefSimple<V>
: UnwrapRefSimple<T>
export type UnwrapRefSimple<T> = T extends
| Function
| BaseTypes
| 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>>> // Omit忽略,表示以某个类型为基础剔除某些属性,返回一个新的类型
: 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