Vue3源码系列——Ref

668 阅读3分钟

导出了什么?

export {
  ref,
  shallowRef,
  isRef,
  toRef,
  toRefs,
  unref,
  customRef,
  triggerRef,
  Ref,
  ToRef,
  ToRefs,
  UnwrapRef,
  ShallowUnwrapRef,
  RefUnwrapBailTypes
} from './ref'

公共部分

1、创建Ref

function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) {
    // 被代理过直接return
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}

2、创建Ref的类

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

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

  constructor(value: T, public readonly _shallow: boolean) {
    this._rawValue = _shallow ? value : toRaw(value)
    this._value = _shallow ? value : toReactive(value)
  }
// toRaw:const raw = observed && (observed as Target)[ReactiveFlags.RAW]
//        return raw ? toRaw(raw) : observed
// toReactive:isObject(value) ? reactive(value) : value
//        return raw ? toRaw(raw) : observed

  get value() {
    // 收集依赖
    trackRefValue(this)
    return this._value
  }

  set value(newVal) {
    newVal = this._shallow ? newVal : toRaw(newVal)
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = this._shallow ? newVal : toReactive(newVal)
      // 触发依赖
      triggerRefValue(this, newVal)
    }
  }
}

3、type和接口,不细讲了

Ref,
ToRef,
ToRefs,
UnwrapRef,
ShallowUnwrapRef,
RefUnwrapBailTypes

ref

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

核心

export function ref(value?: unknown) {
  return createRef(value, false)
}

细节

  1. 第二个参数shallow=false
  2. this._rawValue = toRaw(value) 被代理过:数据还原;未被代理:返回自身 this._value = toReactive(value) 对象:进行代理;原始值:返回自身
  3. get收集依赖,返回_value
  4. newVal:被代理过:数据还原;未被代理:返回自身
  5. hasChanged对比前后数据
  6. 更新_rawValue、_value,触发依赖
constructor(value: T, public readonly _shallow: boolean) {
  this._rawValue = _shallow ? value : toRaw(value)
  this._value = _shallow ? value : toReactive(value)
}
get value() {
  trackRefValue(this)
  return this._value
}

set value(newVal) {
  newVal = this._shallow ? newVal : toRaw(newVal)
  if (hasChanged(newVal, this._rawValue)) {
    this._rawValue = newVal
    this._value = this._shallow ? newVal : toReactive(newVal)
    triggerRefValue(this, newVal)
  }
}

shallowRef

创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的。

核心

function shallowRef(value) {
  return createRef(value, true)
}

细节

  1. 第二个参数shallow=true
  2. this._rawValue = value this._value = value
  3. get收集依赖,返回_value
  4. hasChanged对比前后数据
  5. 更新_rawValue、_value,触发依赖
constructor(value: T, public readonly _shallow: boolean) {
  this._rawValue = _shallow ? value : toRaw(value)
  this._value = _shallow ? value : toReactive(value)
}
get value() {
  trackRefValue(this)
  return this._value
}

set value(newVal) {
  newVal = this._shallow ? newVal : toRaw(newVal)
  if (hasChanged(newVal, this._rawValue)) {
    this._rawValue = newVal
    this._value = this._shallow ? newVal : toReactive(newVal)
    triggerRefValue(this, newVal)
  }
}

ref和shallowRef区别

  • ref:this._rawValue存储数据本身,this._value存储被代理后的数据
  • shallowRef:this._rawValue、this._value都存储数据本身

参数

  • 原始值:.value获取的都是数据本身,没有区别;
  • 引用值:ref的.value返回代理后的数据,shallowRef的.value返回数据本身

isRef

检查值是否为一个 ref 对象。

核心

公共部分2第6行代码,被ref过的数据会有__v_isRef=true

function isRef(r) {
  return Boolean(r && r.__v_isRef === true)
}

toRef

可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接

核心

把ref和对象的一个属性进行链接

function toRef(object, key) {
  const val = object[key]
  return isRef(val) ? val : new ObjectRefImpl(object, key) 
}

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

  constructor(private readonly _object: T, private readonly _key: K) {}

  get value() {
    return this._object[this._key]
  }

  set value(newVal) {
    this._object[this._key] = newVal
  }
}

toRefs

将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref

核心

  • 参数必须是被代理过的,否则回报错
  • 构建对象或数组,把每一项进行toRef
function toRefs(object) {
  if (__DEV__ && !isProxy(object)) {
    console.warn(`toRefs() expects a reactive object but received a plain one.`)
  }
  const ret = isArray(object) ? new Array(object.length) : {}
  for (const key in object) {
    ret[key] = toRef(object, key)
  }
  return ret
}

unref

如果参数是一个 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 的语法糖函数。

核心

是ref就return它的value

function unref(ref) {
  return isRef(ref) ? ref.value : ref
}

customRef

创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收 tracktrigger 函数作为参数,并且应该返回一个带有 getset 的对象。

核心

自定义Ref的get、set

function customRef(factory) {
  return new CustomRefImpl(factory)
}
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) {
    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)
  }
}

triggerRef

手动执行与 shallowRef 关联的任何作用 (effect)。

例子:shallowRef,参数是对象{greet: 'Hello, world'},执行shallow.value.greet = 'Hello, universe',不会触发依赖,调用triggerRef(ref),就能触发依赖

核心

触发一次依赖

function triggerRef(ref: Ref) {
  triggerRefValue(ref, __DEV__ ? ref.value : void 0)
}
function triggerRefValue(ref, newVal) {
  ref = toRaw(ref)
  if (ref.dep) {
    if (__DEV__) {
      triggerEffects(ref.dep, {
        target: ref,
        type: TriggerOpTypes.SET,
        key: 'value',
        newValue: newVal
      })
    } else {
      triggerEffects(ref.dep)
    }
  }
}