共计 3980 个字符,预计需要花费 10 分钟才能阅读完成。
【vue3 源码】九、ref 源码解析
参考代码版本:vue 3.2.37
官网文档:https://vuejs.org/
ref
承受一个外部值,返回一个响应式的、可更改的ref
对象,此对象只有一个指向其外部值的 property.value
应用
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
源码解析
export function ref(value?: unknown) {return createRef(value, false)
}
ref
返回 createRef
函数的返回值。
createRef
接管两个参数:rawValue
待转换的值、shallow
浅层响应式。
function createRef(rawValue: unknown, shallow: boolean) {if (isRef(rawValue)) {return rawValue}
return new RefImpl(rawValue, shallow)
}
如果 rawValue
本就是 ref
类型的会立刻返回 rawValue
,否则返回一个RefImpl
实例。
RefImpl
class RefImpl<T> {
private _value: T
private _rawValue: T
// 以后 ref 的依赖
public dep?: Dep = undefined
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)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = this.__v_isShallow ? newVal : toReactive(newVal)
triggerRefValue(this, newVal)
}
}
}
RefImpl
的结构器接管两个值:value
、__v_isShallow
是否浅层响应式。
constructor(value: T, public readonly __v_isShallow: boolean) {
// 获取原始值,如果是浅层响应式,原始值就是 value;如果不是浅层响应式,原始值是 value 的原始值
this._rawValue = __v_isShallow ? value : toRaw(value)
// 响应式数据,如果是浅层响应式,是 value;否则转为 reactive(只有 Object 类型才会转为 reactive)this._value = __v_isShallow ? value : toReactive(value)
}
当获取 new RefImpl()
的value
属性时,会调用 trackRefValue
进行依赖收集,并返回this._value
。
export function trackRefValue(ref: RefBase<any>) {if (shouldTrack && activeEffect) {ref = toRaw(ref)
if (__DEV__) {trackEffects(ref.dep || (ref.dep = createDep()), {
target: ref,
type: TrackOpTypes.GET,
key: 'value'
})
} else {
// 收集依赖到 ref.dep 中
trackEffects(ref.dep || (ref.dep = createDep()))
}
}
}
与 reactive
不同,ref
的依赖会被保留在 ref.dep
中。
当批改 new RefImpl()
的value
属性时,会调用 triggerRefValue
触发依赖。
set value(newVal) {newVal = this.__v_isShallow ? newVal : toRaw(newVal)
// 当 newVal 与旧原始值不同时,触发依赖
if (hasChanged(newVal, this._rawValue)) {
// 更新原始值及响应式数据
this._rawValue = newVal
this._value = this.__v_isShallow ? newVal : toReactive(newVal)
triggerRefValue(this, newVal)
}
}
shallowRef
shallowRef
的实现同样通过 createRef
函数,不过参数 shallow
为true
。
export function shallowRef(value?: unknown) {return createRef(value, true)
}
const state = shallowRef({count: 1})
effect(() => {console.log(state.value.count)
})
// 不会触发副作用
state.value.count = 2
// 能够触发副作用
state.value = {count: 3}
为什么 state.value.count = 2
不触发副作用?state
初始化时,state._value
就是 {count: 1}
,一个一般对象,当应用state.value.count = 2
设置值时,会先触发 get
函数返回 state._value
,而后再批改state._value
,因为state._value
是一般对象,所以不会有副作用触发。
而当应用 state.value = {count: 3}
形式进行批改时,会命中 set
函数,因为新的值与旧的原始值内存地址不同,所以会触发副作用。
triggerRef
强制触发 ref
的副作用函数。
export function triggerRef(ref: Ref) {triggerRefValue(ref, __DEV__ ? ref.value : void 0)
}
实现原理很简略,就是被动调用一下 triggerRefValue
函数。
因为深度响应式的 ref
会主动进行依赖的触发,所以 triggerRef
次要利用于 shallowRef
的外部值进行深度变更后,被动调用 triggerRef
以触发依赖。例如后面的例子:
const state = shallowRef({count: 1})
effect(() => {console.log(state.value.count)
})
// 不会触发副作用
state.value.count = 2
// 被动触发副作用
triggerRef(state)
// 能够主动触发副作用
state.value = {count: 3}
customRef
创立一个自定义的ref
,显式申明对其依赖追踪和更新触发的管制形式。
如创立一个防抖 ref
,即只在最近一次set
调用后的一段固定距离后再调用:
import {customRef} from 'vue'
export function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {get() {track()
return value
},
set(newValue) {clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()}, delay)
}
}
})
}
来看 customRef
的实现:
export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {return new CustomRefImpl(factory) as any
}
customRef
返回一个 CustomRefImpl
实例。
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)
}
}
CustomRefImpl
的实现与 RefImpl
的实现差不多,都有个 value
的get
、set
函数,只不过 get
、set
在外部会调用用户本人定义的 get
与set
函数。当进行初始化时,会将收集依赖的函数与触发依赖的函数作为参数传递给factory
,这样用户就能够本人管制依赖收集与触发的机会。
总结
ref
的通过 class
实现,通过 class
的取值函数和存值函数进行依赖的收集与触发。
对于深度响应式的 ref
,会在向value
属性赋值过程中,将新的值转为reactive
,以达到深度响应式的成果。