背景
最近在学习Vue3源码ref模块的的时候,一直好奇为什么ref在js里面需要通过.value去取值赋值。但是在模板中不需要。
然后深入发现在Vue源码中找到了这么一个API。就是今天要讨论的proxyRefs。
proxyRefs主要是在setup中的return中对返回结果做了处理。所以我们在模板语法中可以不需要使用.value。
源码中是如何实现的
我们看下Vue3源码中的handleSetupResult。代码目录为src/components。这一段代码就是对setup中我们return出去的一些变量或者方法的处理。其中还包含漏写return的时候的警告。setup() should return an object.
export function handleSetupResult(
instance: ComponentInternalInstance,
setupResult: unknown,
isSSR: boolean
) {
if (isFunction(setupResult)) {
// setup returned an inline render function
if (__SSR__ && (instance.type as ComponentOptions).__ssrInlineRender) {
// when the function's name is `ssrRender` (compiled by SFC inline mode),
// set it as ssrRender instead.
instance.ssrRender = setupResult
} else {
instance.render = setupResult as InternalRenderFunction
}
} else if (isObject(setupResult)) {
if (__DEV__ && isVNode(setupResult)) {
warn(
`setup() should not return VNodes directly - ` +
`return a render function instead.`
)
}
// setup returned bindings.
// assuming a render function compiled from template is present.
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
instance.devtoolsRawSetupState = setupResult
}
instance.setupState = proxyRefs(setupResult)
if (__DEV__) {
exposeSetupStateOnRenderContext(instance)
}
} else if (__DEV__ && setupResult !== undefined) {
warn(
`setup() should return an object. Received: ${
setupResult === null ? 'null' : typeof setupResult
}`
)
}
finishComponentSetup(instance, isSSR)
}
这句代码就是使用proxyRefs对setupResult做处理。其他部分的代码不在这次的分析范围内~
instance.setupState = proxyRefs(setupResult)
继续沿着往下看,就到了proxyRefs的定义。是在reactivity/ref中定义实现的。我给参数加上一些注解方便大家理解。
- 如果objectWithRefs是一个reactive,则直接返回,因为reactive的值是直接可以get取到值的。
- 如果不是则通过shallowUnwrapHandlers对objectWithRefs做处理。
// objectWithRefs 就是返回的object钟可能包含ref类型的值
export function proxyRefs<T extends object>(
objectWithRefs: T
): ShallowUnwrapRef<T> {
// 如果objectWithRefs是一个reactive,则直接返回,因为reactive的值是直接可以get取到值的。
// 如果不是则通过shallowUnwrapHandlers对objectWithRefs做处理。
return isReactive(objectWithRefs)
? objectWithRefs
: new Proxy(objectWithRefs, shallowUnwrapHandlers)
}
那么关键部分来了,这个handlers是如何对objectWithRefs做的处理让我们在模板中不用.value就能取到值呢?
const shallowUnwrapHandlers: ProxyHandler<any> = {
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)
}
}
}
首先get部分很容易理解。 直接对返回值做了unref处理,这样拿到的值就是ref处理前的值,也就不需要通过.value取了。
set部分。
- 如果新不是ref类型,那么直接改掉oldValue(ref)的value。
- 如果不是则直接替换成新的值返回。
这样就能保证在模板语法中使用ref类型的值不需要使用.value了。