Skip to content

ref

  • ref 函数返回执行 createRef 函数的返回值
  • 在 createRef 内部 处理了嵌套 ref 的情况 和已经是响应式对象的处理 直接返回
  • 接着返回了 RefImpl 对象的实例

ref 的优化

  • 主要是对 ref 对象的 value 属性执行依赖收集和派发通知的逻辑
  • 在 3.2 版本 ref 的实现中 依赖收集部分由原先的 track 函数 变成了 trackRefValue
ts

export function ref<T extends object>(
  value: T
): [T] extends [Ref] ? T : Ref<UnwrapRef<T>>
export function ref<T>(value: T): Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
export function ref(value?: unknown) {
  return createRef(value, false)
}

function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) {
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}

class RefImpl<T> {
  private _value: T
  private _rawValue: T

  public dep?: Dep = undefined
  public readonly __v_isRef = true

  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) {
    const useDirectValue =
      this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
    newVal = useDirectValue ? newVal : toRaw(newVal)
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = useDirectValue ? newVal : toReactive(newVal)
      triggerRefValue(this, newVal)
    }
  }
}

trackRefValue

  • 直接把 ref 相关依赖保存到 dep 属性中 在 track 函数的实现 会把依赖保存在 全局的 targetMap 中
  • track 函数内部可能需要多次判断和设置逻辑 而把依赖保存到 ref 对象的 dep 属性中省去了一系列判断和设置 优化了性能
  • ref 实现的派发通知的部分由原先的 trigger 函数 变成了 triggerRefValue 中
  • 由于可以直接从 ref 属性中获取其所有的依赖并且遍历执行 不需要执行 trigger 函数的额外逻辑 性能提高
ts
export function trackRefValue(ref: RefBase<any>) {
  if (shouldTrack && activeEffect) {
    ref = toRaw(ref)
    if (__DEV__) {
      trackEffects(ref.dep || (ref.dep = createDep()), {
        target: ref,
        type: TrackOpTypes.GET,
        key: 'value'
      })
    } else {
      trackEffects(ref.dep || (ref.dep = createDep()))
    }
  }
}

unref

  • template 中访问 响应式对象 不需要 .value 访问 而是在 setupResult 处理的时候 赋值给组件实例中的 setupState 属性
  • 因为初始化的过程中会创建渲染上下文代理 所以在 render 函数中访问 _ctx.xxx 就相当于 访问了 instance.setupState.xxx
  • unref 的实现就是判断是不是响应式对象 如果是 直接返回 响应式的对象的 value 值 不是就直接返回 原对象
ts
export function unref<T>(ref: T | Ref<T>): T {
  return isRef(ref) ? (ref.value as any) : ref
}

Welcome to the site