Vue 3 Ref源码解析 (pre-alpha)

940 阅读3分钟

上一篇文章说了一下reactive的相关信息,这一节来看看Vue 3中ref的使用方法及相关信息

附上一篇文章链接:juejin.cn/post/684490…

为了理解ref的逻辑顺序,就直接从ref核心函数一步一步扩展讲解吧~

export function ref<T>(raw: T): Ref<T> {  
    raw = convert(raw)  
    const v = {    
      _isRef: true,    
      get value() {
      track(v, OperationTypes.GET, '')
      return raw
    },
    set value(newVal) {
      raw = convert(newVal)
      trigger(v, OperationTypes.SET, '')
    }
  }
  return v as Ref<T>
}

首先,声明了一个ref,该函数类型为Ref类型,Ref<T>是一个接口,源码如下:

export interface Ref<T> {
  _isRef: true
  value: UnwrapNestedRefs<T>
}

ref的核心就是判断_isRef是否为true,value值则是存放数据的地方

UnwrapNestedRefs是在判断T(类型)如果是继承了Ref则使用当前类型,如果不是,使用UnwrapRef类型,源码如下:

export type UnwrapNestedRefs<T> = T extends Ref<any> ? T : UnwrapRef<T>

UnwrapRef作用就是当遇到嵌套Ref的时候能正确(使用递归)推出业务数据的类型

注:Vue 源码中手动写了递归解析案例

⭐因手动写的案例代码多,仅截取前两段代码(未截取的代码都是类似)

export type UnwrapRef<T> = T extends Ref<infer V>
  ? UnwrapRef2<V>
  : T extends Array<infer V>
    ? Array<UnwrapRef2<V>>
    : T extends BailTypes
      ? T
       : T extends object ? { [K in keyof T]: UnwrapRef2<T[K]> } : T

type UnwrapRef2<T> = T extends Ref<infer V> 
 ? UnwrapRef3<V>
  : T extends Array<infer V>
    ? Array<UnwrapRef3<V>>
    : T extends BailTypes
      ? T
      : T extends object ? { [K in keyof T]: UnwrapRef3<T[K]> } : T

:能够递归解析的类型只有ref类型,Array和Object,如果是bailTypes中的类型,则停止

bailTypes源码:

type BailTypes =
  | Function
  | Map<any, any>
  | Set<any>
  | WeakMap<any, any>
  | WeakSet<any>

回到ref的源码,介绍了Ref接口之后,接下来就是对传入的参数进行处理

传入的参数会被传入convert中进行处理,源码如下:

const convert = (val: any): any => (isObject(val) ? reactive(val) : val)

即,如果是对象(复杂类型、多层)会将传入的值交由reactive做响应式处理,不是对象,则返回传入的值

接下来创建一个v对像,其中传入_isRef:true(之前说过判断是否未ref类型的关键就是isRef)

判断isRef的源码如下:

export function isRef(v: any): v is Ref<any> {
  return v ? v._isRef === true : false
}

接着就是v中的get和set方法

const v = {
   _isRef: true,
   get value() {
     track(v, OperationTypes.GET, '')
     return raw
   },
   set value(newVal) {
     raw = convert(newVal)
     trigger(v, OperationTypes.SET, '')
   }
}

get方法中有一个track方法,track的作用就是监听并收集依赖

set方法中就是用convert将newVal值转换为响应式赋值给raw,trigger的作用就是执行函数

注:上面的Convert中对于将复杂类型(Object)利用reactive转为响应式,而对于简单类型数据则没有进行相关处理,是因为当我们在将参数在函数中传递或者解构参数时,会丢失原始数据的引用,因此(如上图v对象所示),直接用set和get方法对值进行拦截就可以,而不用再采取Proxy来进行拦截处理

另外,Vue 还提供了一个toRefs的方法,将传入的对象的key分别存入v对象,将_isRef设置为true,接着将处理后的key赋值给一个新对象ret

源码如下:

export function toRefs<T extends object>(
  object: T
): {[K in keyof T]: Ref<T[K]> } {
  const ret: any = {}
  for (const key in object) {
    ret[key] = toProxyRef(object, key)
 }
 return ret
}

function toProxyRef<T extends object, K extends keyof T>(
  object: T,  key: K
): Ref<T[K]> {
  const v = {
    _isRef: true,
    get value() {
      return object[key]
    },
    set value(newVal) {
      object[key] = newVal
    }
  }
  return v as Ref<T[K]>
}

ref的相关介绍就到这里拉~,这是我自己对Vue 3中ref的一些理解,如果哪里有错误的,感谢大家指出拉~~