【vue3源码】九、ref源码解析
参考代码版本:vue 3.2.37
官网文档:https://vuejs.org/
ref
承受一个外部值,返回一个响应式的、可更改的ref
对象,此对象只有一个指向其外部值的property.value
应用
const count = ref(0)console.log(count.value) // 0count.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
,以达到深度响应式的成果。