本文涉及到的响应式类型判断可以参考 [vue3] isRef、isProxy、isReactive、isReadonly
我们在使用ref 定义变量时(const a = ref(null)), 首先会判断是不是 ref 类型,如果是直接返回,如果不是 返回 RefImpl 实例。我们每次定义的变量,如果是普通类型的值,那么直接返回这个值,如果是复杂类型,会通过 reactive 来转变为响应式数据。
-
由于本文只是围绕 普通类型的值 展开讨论,可以直接认为 toRaw, toReactive 直接返回当前值就可以了
-
toReactive 实现,首先判断是不是 Object(value !==null && typeof value === ‘object’),如果是,执行reactive,否则直接返回
-
后续章节在进行详细的介绍 toRaw、reactive
// core/packages/reactivity/src/ref.ts
export function ref(value?: unknown) {
return createRef(value, false)
}
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
class RefImpl<T> {
private _value: T
private _rawValue: T
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)
}
...
}
// core/packages/reactivity/src/reactive.ts
export const toReactive = <T extends unknown>(value: T): T =>
isObject(value) ? reactive(value) : value
当我们执行 a.value 时会执行get 方法。
// core/packages/reactivity/src/ref.ts
class RefImpl<T> {
....
get value() {
trackRefValue(this)
return this._value
}
....
}
首先会跟踪ref 的value,跟踪副作用。然后返回当前 _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 {
trackEffects(ref.dep || (ref.dep = createDep()))
}
}
}
当我们执行 a.value = 2,执行 set 操作。
首先判断新旧两个值是不是相同,如果不相同那么 _rawValue, _value 记录当前新的值。执行triggerRefValue
// core/packages/reactivity/src/ref.ts
class RefImpl<T> {
...
set value(newVal) {
const useDirectValue =
this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
newVal = useDirectValue ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = useDirectValue ? newVal : toReactive(newVal)
triggerRefValue(this, newVal)
}
}
}
由于目前 ref.dep 此时为undifined我们目前可以认为 triggerRefValue 什么也没有干
export function triggerRefValue(ref: RefBase<any>, newVal?: any) {
ref = toRaw(ref)
const dep = ref.dep
if (dep) {
if (__DEV__) {
triggerEffects(dep, {
target: ref,
type: TriggerOpTypes.SET,
key: 'value',
newValue: newVal
})
} else {
triggerEffects(dep)
}
}
}