Vue3 的响应式系统源码级 ref 的实现原理与自动脱 ref 机制
字数 1000 2025-11-17 15:47:01
Vue3 的响应式系统源码级 ref 的实现原理与自动脱 ref 机制
一、ref 的设计目标与使用场景
ref 主要用于处理基本类型的响应式数据(如 number、string、boolean),也可用于引用类型。与 reactive 使用 Proxy 代理对象不同,ref 通过封装值为对象来实现响应式,核心原理是依赖收集和触发更新。
二、ref 实现的核心步骤
-
创建 ref 对象
function ref(value) { return createRef(value, false) }- 接收初始值,调用
createRef函数 - 第二个参数
false表示非浅响应式(shallowRef 为 true)
- 接收初始值,调用
-
createRef 内部实现
function createRef(rawValue, shallow) { if (isRef(rawValue)) return rawValue // 避免重复包装 return new RefImpl(rawValue, shallow) }- 检查传入值是否已是 ref,避免重复包装
- 实例化 RefImpl 类完成核心逻辑
-
RefImpl 类的核心结构
class RefImpl<T> { private _value: T private _rawValue: T // 存储原始值用于比较 public readonly __v_isRef = true // ref 标识符 public dep?: Dep // 依赖收集器 constructor(value: T, public readonly __v_isShallow: boolean) { this._rawValue = value // 根据是否为浅响应式决定如何存储值 this._value = __v_isShallow ? value : toReactive(value) } get value() { trackRefValue(this) // 依赖收集 return this._value } set value(newVal) { if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = toReactive(newVal) triggerRefValue(this) // 触发更新 } } }
三、关键辅助函数详解
-
toReactive 函数
const toReactive = <T extends unknown>(value: T): T => isObject(value) ? reactive(value) : value- 如果新值是对象,则用 reactive 包装
- 基本类型直接返回
-
hasChanged 函数
const hasChanged = (value: any, oldValue: any): boolean => !Object.is(value, oldValue)- 使用 Object.is 进行精确比较(处理 NaN 等特殊情况)
-
依赖收集 trackRefValue
export function trackRefValue(ref: RefBase<any>) { if (shouldTrack && activeEffect) { ref = toRaw(ref) trackEffects(ref.dep || (ref.dep = createDep())) } }- 创建依赖集合(不存在时)
- 将当前 activeEffect(副作用函数)加入依赖
-
触发更新 triggerRefValue
export function triggerRefValue(ref: RefBase<any>) { ref = toRaw(ref) if (ref.dep) { triggerEffects(ref.dep) } }- 遍历 dep 中的所有 effect 执行更新
四、自动脱 ref(unref)机制
-
模板中的自动脱 ref
<template> <div>{{ count }}</div> <!-- 无需 .value --> </template>- 编译器在模板中自动添加
.value访问
- 编译器在模板中自动添加
-
响应式系统级 unref 实现
export function unref<T>(ref: T): T extends Ref<infer V> ? V : T { return isRef(ref) ? ref.value : ref }- 检查参数是否为 ref,是则返回
.value,否则直接返回
- 检查参数是否为 ref,是则返回
-
toRefs 的自动脱 ref 支持
function toRefs<T extends object>(object: T) { const ret: any = isArray(object) ? new Array(object.length) : {} for (const key in object) { ret[key] = propertyToRef(object, key) } return ret }- 将 reactive 对象的每个属性转换为 ref
- 结合 unref 实现解构时的自动脱 ref
五、特殊 ref 类型处理
-
浅层 ref(shallowRef)
function shallowRef(value) { return createRef(value, true) }- 仅对
.value的赋值响应式,不递归转换嵌套对象
- 仅对
-
toRef 实现
function toRef(source, key) { return new ObjectRefImpl(source, key) }- 为 reactive 对象的特定属性创建 ref
- 保持与源属性的链接关系
六、ref 的性能优化策略
-
依赖收集的懒初始化
- dep 在首次被访问时才创建,减少内存开销
-
值变化的精确判断
- 通过
hasChanged避免不必要的更新触发
- 通过
-
嵌套值的懒响应式转换
- 只有在访问时才会递归应用 reactive
这种实现保证了 ref 在保持响应式能力的同时,提供了良好的类型支持和性能表现,特别是自动脱 ref 机制大大提升了开发体验。