[Vue3 源码阅读] ref

234 阅读2分钟

一、ref介绍

ref是vue3中的一个响应式api,官方文档中对其描述是:

其接受一个内部值并返回一个响应式且可变的ref对象。ref对象具有指向内部值得单个property.value

直白的说,就是ref是一个函数,其可接收一个参数并会返回一个响应式的对象,此响应式对象有一个value属性,value属性的值指向传入的参数,访问和修改value属性等同于修改原始参数,只是ref提供了响应式能力。

二、源码分析

// packages\reactivity\src\ref.ts

// ref函数的定义
export function ref<T extends object>(value: T): ToRef<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)
}

// createRef函数定义
function createRef(rawValue: unknown, shallow = false) {
  // 如果传入的值已经是ref类型,直接返回该值,不做处理
  if (isRef(rawValue)) {
    return rawValue
  }
  // 实例化一个RefImpl对象,并返回
  return new RefImpl(rawValue, shallow)
}

// RefImpl Class定义
class RefImpl<T> {
  private _value: T

  public readonly __v_isRef = true

  // 构造函数,接受2个参数
  // 参数1:原始值
  // 参数2:是否是shallowRef
  constructor(private _rawValue: T, public readonly _shallow: boolean) {
    this._value = _shallow ? _rawValue : convert(_rawValue)
  }
  
  // value属性的get方法
  get value() {
    // get时进行了一次track(即:依赖收集)
    track(toRaw(this), TrackOpTypes.GET, 'value')
    // get时返回的是原始值
    return this._value
  }

  // value属性的set方法
  set value(newVal) {
    // 判断值是否发生更改,采用!==运算符比较
    if (hasChanged(toRaw(newVal), this._rawValue)) {
      // 更改原始值
      this._rawValue = newVal
      this._value = this._shallow ? newVal : convert(newVal)
      // 触发trigger(即:更新通知)
      trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
    }
  }
}

三、总结

ref的源码不多,整个调用过程很清晰:ref函数->调用createRef函数->实例化RefImpl并返回;在实例化RefImpl时,通过设置value属性的get和set方法,进行依赖的收集和更新通知,核心在于tracktrigger方法如何进行依赖收集和更新通知。