上一篇文章说了一下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的一些理解,如果哪里有错误的,感谢大家指出拉~~