前言
首先 ref
的出现是为了解决原始值的响应式问题
使用
<script setup>
const refData = ref(1)
refData.value = 2 // 触发响应式更新
console.log(refData.value) // 2
</script>
<template>
<div>
{{ refData }}
</div>
</template>
这里发现有些不同的地方,我们在模板中使用不加 value
, 而在 js 里面需要带上 value,为了搞清楚这里面的关系,我们去源码里一探究竟
具体实现
具体的实现在源码里 packages\reactivity\src\ref.ts
export function ref(value?: unknown) {
return createRef(value, false)
}
function createRef(rawValue: unknown, shallow: boolean) {
// 当使用 ref(1) 时,此时 isRef 还是 false;
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
class RefImpl<T> {
private _value: T
private _rawValue: T
// 这个是收集 effect 函数的 Set
public dep?: Dep = undefined
// 这里 把 __v_isRef 设置 为 true,表示这时一个 ref 数据,用以和 reactive 数据作区分
public readonly __v_isRef = true
constructor(value: T, public readonly __v_isShallow: boolean) {
this._rawValue = __v_isShallow ? value : toRaw(value)
// 这里会走到 toReactive(value),也就是调用 reactive(value),
// 这个主要针对的是如果传入的是对象的情况
this._value = __v_isShallow ? value : toReactive(value)
}
// 对于 原始值,
get value() {
// 当调用 refData.value 会执行这里,
trackRefValue(this) // 收集当前对象到 dep 中去
return this._value
}
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 // 这里触发更新,当 执行refData.value = newVal 时更新 effect。
}
}
}
上面解决了为什么在 js 中调用 ref
需要加 value
的问题,下面需要解决模板中不用 value
的问题
export function unref<T>(ref: T | Ref<T>): T {
return isRef(ref) ? (ref.value as any) : ref
}
const shallowUnwrapHandlers: ProxyHandler<any> = {
// 这里会根据我们在创建 ref 时定义的 __v_isRef = true 来进行脱 value 处理
// 如果是 ref 数据,那么访问 refData 直接返回 refData.value;
get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)),
set: (target, key, value, receiver) => {
const oldValue = target[key]
// 这里 设置时也一样 如果是 ref 数据, refData = nVal 相当于 refData.value = nVal
if (isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
} else {
return Reflect.set(target, key, value, receiver)
}
}
}
export function proxyRefs<T extends object>(
objectWithRefs: T
): ShallowUnwrapRef<T> {
return isReactive(objectWithRefs)
? objectWithRefs
: new Proxy(objectWithRefs, shallowUnwrapHandlers)
}
上面是拦截 ref
数据的 getter
和 setter
操作,在处理 setup数据时会调用 proxyRefs
数据
instance.setupState = proxyRefs(setupResult)
最终我们理解了 ref
的实现
总结
ref
主要是解决原始值的响应式问题ref
的实现就是把传入ref
的数据用一个对象包起来,并且记录下数据是否是ref数据,然后调用reactive
,就类似下面这样
function ref(val) {
const wrapper = {
value: val
}
Object.defineProperty(wrapper, 'isRef', { value: true })
return reactive(wrapper)
}
- 模板中使用不用加
value
是因为调用了proxyRefs
脱了value
有一些细节是猜的,但是大概应该没问题,过路大佬如果有问题多多指教,谢谢!