Vue3 的响应式系统源码级 ref 的实现原理与自动脱 ref 机制
字数 1000 2025-11-17 15:47:01

Vue3 的响应式系统源码级 ref 的实现原理与自动脱 ref 机制

一、ref 的设计目标与使用场景
ref 主要用于处理基本类型的响应式数据(如 number、string、boolean),也可用于引用类型。与 reactive 使用 Proxy 代理对象不同,ref 通过封装值为对象来实现响应式,核心原理是依赖收集和触发更新。

二、ref 实现的核心步骤

  1. 创建 ref 对象

    function ref(value) {
      return createRef(value, false)
    }
    
    • 接收初始值,调用 createRef 函数
    • 第二个参数 false 表示非浅响应式(shallowRef 为 true)
  2. createRef 内部实现

    function createRef(rawValue, shallow) {
      if (isRef(rawValue)) return rawValue  // 避免重复包装
      return new RefImpl(rawValue, shallow)
    }
    
    • 检查传入值是否已是 ref,避免重复包装
    • 实例化 RefImpl 类完成核心逻辑
  3. 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)  // 触发更新
        }
      }
    }
    

三、关键辅助函数详解

  1. toReactive 函数

    const toReactive = <T extends unknown>(value: T): T =>
      isObject(value) ? reactive(value) : value
    
    • 如果新值是对象,则用 reactive 包装
    • 基本类型直接返回
  2. hasChanged 函数

    const hasChanged = (value: any, oldValue: any): boolean =>
      !Object.is(value, oldValue)
    
    • 使用 Object.is 进行精确比较(处理 NaN 等特殊情况)
  3. 依赖收集 trackRefValue

    export function trackRefValue(ref: RefBase<any>) {
      if (shouldTrack && activeEffect) {
        ref = toRaw(ref)
        trackEffects(ref.dep || (ref.dep = createDep()))
      }
    }
    
    • 创建依赖集合(不存在时)
    • 将当前 activeEffect(副作用函数)加入依赖
  4. 触发更新 triggerRefValue

    export function triggerRefValue(ref: RefBase<any>) {
      ref = toRaw(ref)
      if (ref.dep) {
        triggerEffects(ref.dep)
      }
    }
    
    • 遍历 dep 中的所有 effect 执行更新

四、自动脱 ref(unref)机制

  1. 模板中的自动脱 ref

    <template>
      <div>{{ count }}</div>  <!-- 无需 .value -->
    </template>
    
    • 编译器在模板中自动添加 .value 访问
  2. 响应式系统级 unref 实现

    export function unref<T>(ref: T): T extends Ref<infer V> ? V : T {
      return isRef(ref) ? ref.value : ref
    }
    
    • 检查参数是否为 ref,是则返回 .value,否则直接返回
  3. 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 类型处理

  1. 浅层 ref(shallowRef)

    function shallowRef(value) {
      return createRef(value, true)
    }
    
    • 仅对 .value 的赋值响应式,不递归转换嵌套对象
  2. toRef 实现

    function toRef(source, key) {
      return new ObjectRefImpl(source, key)
    }
    
    • 为 reactive 对象的特定属性创建 ref
    • 保持与源属性的链接关系

六、ref 的性能优化策略

  1. 依赖收集的懒初始化

    • dep 在首次被访问时才创建,减少内存开销
  2. 值变化的精确判断

    • 通过 hasChanged 避免不必要的更新触发
  3. 嵌套值的懒响应式转换

    • 只有在访问时才会递归应用 reactive

这种实现保证了 ref 在保持响应式能力的同时,提供了良好的类型支持和性能表现,特别是自动脱 ref 机制大大提升了开发体验。

Vue3 的响应式系统源码级 ref 的实现原理与自动脱 ref 机制 一、ref 的设计目标与使用场景 ref 主要用于处理基本类型的响应式数据(如 number、string、boolean),也可用于引用类型。与 reactive 使用 Proxy 代理对象不同,ref 通过封装值为对象来实现响应式,核心原理是依赖收集和触发更新。 二、ref 实现的核心步骤 创建 ref 对象 接收初始值,调用 createRef 函数 第二个参数 false 表示非浅响应式(shallowRef 为 true) createRef 内部实现 检查传入值是否已是 ref,避免重复包装 实例化 RefImpl 类完成核心逻辑 RefImpl 类的核心结构 三、关键辅助函数详解 toReactive 函数 如果新值是对象,则用 reactive 包装 基本类型直接返回 hasChanged 函数 使用 Object.is 进行精确比较(处理 NaN 等特殊情况) 依赖收集 trackRefValue 创建依赖集合(不存在时) 将当前 activeEffect(副作用函数)加入依赖 触发更新 triggerRefValue 遍历 dep 中的所有 effect 执行更新 四、自动脱 ref(unref)机制 模板中的自动脱 ref 编译器在模板中自动添加 .value 访问 响应式系统级 unref 实现 检查参数是否为 ref,是则返回 .value ,否则直接返回 toRefs 的自动脱 ref 支持 将 reactive 对象的每个属性转换为 ref 结合 unref 实现解构时的自动脱 ref 五、特殊 ref 类型处理 浅层 ref(shallowRef) 仅对 .value 的赋值响应式,不递归转换嵌套对象 toRef 实现 为 reactive 对象的特定属性创建 ref 保持与源属性的链接关系 六、ref 的性能优化策略 依赖收集的懒初始化 dep 在首次被访问时才创建,减少内存开销 值变化的精确判断 通过 hasChanged 避免不必要的更新触发 嵌套值的懒响应式转换 只有在访问时才会递归应用 reactive 这种实现保证了 ref 在保持响应式能力的同时,提供了良好的类型支持和性能表现,特别是自动脱 ref 机制大大提升了开发体验。