我们知道 vue3 中 template
中使用 ref
类型时候,不需要使用.value
访问,vue 会为我们自动脱ref
,那么在源码层面,这是怎么实现的呢?
刚开始用到这个特性的时候,最自然的想法这是 vue 在模板编译的时候,帮我们自动加上了 .value
,但查看源码,会让我们大跌眼镜,
实际上是在 setup 返回 ref 响应式数据的时候,vue 用proxyRefs
函数对返回的数据又"包了一层",就像使用了一个代理对象,代理了对 ref 响应式数据的访问和设置。
接下来我们来看下源码(版本 3.2.3),
proxyRef 函数 在 vue/core 源码下 reactivity 子包下的 refs.ts 中:
typescript
const shallowUnwrapHandlers: ProxyHandler<any> = {
// 调用unref
get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)),
set: (target, key, value, receiver) => {
const oldValue = target[key]
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> {
// reactive类型直接返回,否则用返回一个proxy
return isReactive(objectWithRefs)
? objectWithRefs
: new Proxy(objectWithRefs, shallowUnwrapHandlers)
}
在 runtime-core 包下的 componet.ts 中,调用了 proxyRefs 方法构造 setup 的返回体:
typescript
export function handleSetupResult(
instance: ComponentInternalInstance,
setupResult: unknown,
isSSR: boolean,
): void {
if (isFunction(setupResult)) {
// .....
} else if (isObject(setupResult)) {
if (__DEV__ && isVNode(setupResult)) {
/// ....
instance.setupState = proxyRefs(setupResult)
// ....
}
finishComponentSetup(instance, isSSR)
}
可以看到我们在 setup 中返回的返回体,最终会被proxyRefs
函数处理后,返会给模板中使用,比如:
javascript
export default{
setup(){
return{
a: ref(1)
}
}
}
最终再模板中使用的其实是proxyRefs
函数处理后的代理对象:
javascript
proxyRefs(
{
a: ref(1)
}
)
观察proxyRefs
源码,我们可以看到它是进行了一次浅代理,即只进行第一层对象的代理,