Vue3 源码阅读,ref及相关函数

186 阅读3分钟

源码版本:3.2.37

源码相对路径:packages/reactivity/src/ref.ts

参考 Vue3 组合式API,ref 相关函数有:

响应式:工具

  • isRef
  • unref
  • toRef
  • toRefs

响应式:核心

  • ref

响应式:进阶

  • shallowRef
  • triggerRef
  • customRef

工具API

isRef

该函数判断传入值的属性 __v_isRef 是否为 true

export function isRef(r: any): r is Ref {
  return !!(r && r.__v_isRef === true)
}

unref

该函数判断传入值是 ref对象 的情况下,返回 ref.value,否则返回传入值

export function unref<T>(ref: T | Ref<T>): T {
  return isRef(ref) ? (ref.value as any) : ref
}

toRef

该函数判断对象属性值是 ref对象 的情况下,返回对象属性值,否则返回 ObjectRefImpl实例。可提供 defaultValue 用于对象属性值未定义的情况

ObjectRefImpl实例 通过 getter/setter 代理访问对象属性值,取值操作 o.p 等于 (toRef(o, 'p')).value;赋值操作 o.p = v 等于 (toRef(o, 'p')).value = v。对 普通对象属性 使用 toRef 生成的对象,不具备响应式操作

export function toRef<T extends object, K extends keyof T>(
  object: T,
  key: K,
  defaultValue?: T[K]
): ToRef<T[K]> {
  const val = object[key]
  return isRef(val)
    ? val
    : (new ObjectRefImpl(object, key, defaultValue) as any)
}

class ObjectRefImpl<T extends object, K extends keyof T> {
  public readonly __v_isRef = true

  constructor(
    private readonly _object: T,
    private readonly _key: K,
    private readonly _defaultValue?: T[K]
  ) {}

  get value() {
    // 获取对象属性值
    const val = this._object[this._key]
    return val === undefined ? (this._defaultValue as T[K]) : val
  }

  set value(newVal) {
    // 设置对象属性值
    this._object[this._key] = newVal
  }
}

toRefs

该函数接收一个响应式对象,然后创建一个相同原始类型的对象,将每个属性值进行 toRef 处理并赋值到新的对象上,最后返回该对象

export function toRefs<T extends object>(object: T): ToRefs<T> {
  if (__DEV__ && !isProxy(object)) {
    console.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] = toRef(object, key)
  }
  return ret
}

核心API

ref

该函数判断传入值是 ref对象 的情况下,直接返回传入值,否则返回 RefImpl实例

RefImpl实例 初始化时会使用 toRaw 函数获取传入值的原始值(非响应式对象)并且赋值到 _rawValue 属性,用于调用 setter时 判断 ref对象 是否需要更新值;然后使用 toReactive 函数将传入值转化为响应式对象并且赋值到 _value 属性,它就是 getter/setter 操作的属性

在之后访问操作 refInstance.value 时,会调用 trackRefValue 收集 ReactiveEffect实例添加到 dep 属性;赋值操作 refInstance.value = v 时,_rawValue 对比不同后会更新 _rawValue_value 值并且调用 triggerRefValue 通知依赖此实例的 ReactiveEffect实例,从而实现响应式操作

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 /* ReactiveEffect实例集合 */
  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) {
    newVal = this.__v_isShallow ? newVal : toRaw(newVal)
    // const hasChanged = (v, oV) => !Object.is(v, oV)
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = this.__v_isShallow ? newVal : toReactive(newVal)
      triggerRefValue(this, newVal)
    }
  }
}

进阶API

shallowRef

该函数与先前的 ref函数 类似,只是在 赋值 _rawValue_value 时,不进行 toRawtoReactive 操作

triggerRef

该函数用于手动触发依赖函数的调用。如果依赖函数包含渲染函数,就相当于进行强制更新

export function triggerRef(ref: Ref) {
  triggerRefValue(ref, __DEV__ ? ref.value : void 0)
}

customRef

该函数传递一个工厂函数,返回 CustomRefImpl实例

CustomRefImpl 初始化时提供 工厂函数 两个函数参数:第一个参数用于 何时追踪依赖函数;第二个参数用于 何时通知依赖函数执行。要求 工厂函数 返回一个包含 getset 属性的对象,在操作 getter/setter 时使用

export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
  return new CustomRefImpl(factory) as any
}

class CustomRefImpl<T> {
  public dep?: Dep = undefined

  private readonly _get: ReturnType<CustomRefFactory<T>>['get']
  private readonly _set: ReturnType<CustomRefFactory<T>>['set']

  public readonly __v_isRef = true

  constructor(factory: CustomRefFactory<T>) {
    const { get, set } = factory(
      () => trackRefValue(this),
      () => triggerRefValue(this)
    )
    this._get = get
    this._set = set
  }

  get value() {
    return this._get()
  }

  set value(newVal) {
    this._set(newVal)
  }
}