refactor(runtime-core): revert setup() result reactive conversion
BREAKING CHANGE: revert setup() result reactive conversion Revert6b10f0c
&a840e7d
. The motivation of the original change was avoiding unnecessary deep conversions, but that can be achieved by explicitly marking values non-reactive via `markNonReactive`. Removing the reactive conversion behavior leads to an usability issue in that plain objects containing refs (which is what most composition functions will return), when exposed as a nested property from `setup()`, will not unwrap the refs in templates. This goes against the "no .value in template" intuition and the only workaround requires users to manually wrap it again with `reactive()`. So in this commit we are reverting to the previous behavior where objects returned from `setup()` are implicitly wrapped with `reactive()` for deep ref unwrapping.
This commit is contained in:
parent
11d2fb2594
commit
e67f655b26
@ -1,5 +1,6 @@
|
|||||||
import { VNode, VNodeChild, isVNode } from './vnode'
|
import { VNode, VNodeChild, isVNode } from './vnode'
|
||||||
import {
|
import {
|
||||||
|
reactive,
|
||||||
ReactiveEffect,
|
ReactiveEffect,
|
||||||
shallowReadonly,
|
shallowReadonly,
|
||||||
pauseTracking,
|
pauseTracking,
|
||||||
@ -398,7 +399,7 @@ export function handleSetupResult(
|
|||||||
}
|
}
|
||||||
// setup returned bindings.
|
// setup returned bindings.
|
||||||
// assuming a render function compiled from template is present.
|
// assuming a render function compiled from template is present.
|
||||||
instance.renderContext = setupResult
|
instance.renderContext = reactive(setupResult)
|
||||||
} else if (__DEV__ && setupResult !== undefined) {
|
} else if (__DEV__ && setupResult !== undefined) {
|
||||||
warn(
|
warn(
|
||||||
`setup() should return an object. Received: ${
|
`setup() should return an object. Received: ${
|
||||||
@ -474,10 +475,6 @@ function finishComponentSetup(
|
|||||||
currentInstance = null
|
currentInstance = null
|
||||||
currentSuspense = null
|
currentSuspense = null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance.renderContext === EMPTY_OBJ) {
|
|
||||||
instance.renderContext = {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to identify a setup context proxy
|
// used to identify a setup context proxy
|
||||||
|
@ -8,14 +8,7 @@ import {
|
|||||||
ComputedOptions,
|
ComputedOptions,
|
||||||
MethodOptions
|
MethodOptions
|
||||||
} from './apiOptions'
|
} from './apiOptions'
|
||||||
import {
|
import { ReactiveEffect, UnwrapRef } from '@vue/reactivity'
|
||||||
ReactiveEffect,
|
|
||||||
isRef,
|
|
||||||
isReactive,
|
|
||||||
Ref,
|
|
||||||
ComputedRef,
|
|
||||||
unref
|
|
||||||
} from '@vue/reactivity'
|
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { Slots } from './componentSlots'
|
import { Slots } from './componentSlots'
|
||||||
import {
|
import {
|
||||||
@ -48,17 +41,11 @@ export type ComponentPublicInstance<
|
|||||||
$nextTick: typeof nextTick
|
$nextTick: typeof nextTick
|
||||||
$watch: typeof instanceWatch
|
$watch: typeof instanceWatch
|
||||||
} & P &
|
} & P &
|
||||||
UnwrapSetupBindings<B> &
|
UnwrapRef<B> &
|
||||||
D &
|
D &
|
||||||
ExtractComputedReturns<C> &
|
ExtractComputedReturns<C> &
|
||||||
M
|
M
|
||||||
|
|
||||||
type UnwrapSetupBindings<B> = { [K in keyof B]: UnwrapBinding<B[K]> }
|
|
||||||
|
|
||||||
type UnwrapBinding<B> = B extends ComputedRef<any>
|
|
||||||
? B extends ComputedRef<infer V> ? V : B
|
|
||||||
: B extends Ref<infer V> ? V : B
|
|
||||||
|
|
||||||
const publicPropertiesMap: Record<
|
const publicPropertiesMap: Record<
|
||||||
string,
|
string,
|
||||||
(i: ComponentInternalInstance) => any
|
(i: ComponentInternalInstance) => any
|
||||||
@ -115,7 +102,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||||||
case AccessTypes.DATA:
|
case AccessTypes.DATA:
|
||||||
return data[key]
|
return data[key]
|
||||||
case AccessTypes.CONTEXT:
|
case AccessTypes.CONTEXT:
|
||||||
return unref(renderContext[key])
|
return renderContext[key]
|
||||||
case AccessTypes.PROPS:
|
case AccessTypes.PROPS:
|
||||||
return propsProxy![key]
|
return propsProxy![key]
|
||||||
// default: just fallthrough
|
// default: just fallthrough
|
||||||
@ -123,9 +110,9 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||||||
} else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
|
} else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
|
||||||
accessCache![key] = AccessTypes.DATA
|
accessCache![key] = AccessTypes.DATA
|
||||||
return data[key]
|
return data[key]
|
||||||
} else if (hasOwn(renderContext, key)) {
|
} else if (renderContext !== EMPTY_OBJ && hasOwn(renderContext, key)) {
|
||||||
accessCache![key] = AccessTypes.CONTEXT
|
accessCache![key] = AccessTypes.CONTEXT
|
||||||
return unref(renderContext[key])
|
return renderContext[key]
|
||||||
} else if (type.props != null) {
|
} else if (type.props != null) {
|
||||||
// only cache other properties when instance has declared (this stable)
|
// only cache other properties when instance has declared (this stable)
|
||||||
// props
|
// props
|
||||||
@ -180,19 +167,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||||||
if (data !== EMPTY_OBJ && hasOwn(data, key)) {
|
if (data !== EMPTY_OBJ && hasOwn(data, key)) {
|
||||||
data[key] = value
|
data[key] = value
|
||||||
} else if (hasOwn(renderContext, key)) {
|
} else if (hasOwn(renderContext, key)) {
|
||||||
// context is already reactive (user returned reactive object from setup())
|
|
||||||
// just set directly
|
|
||||||
if (isReactive(renderContext)) {
|
|
||||||
renderContext[key] = value
|
renderContext[key] = value
|
||||||
} else {
|
|
||||||
// handle potential ref set
|
|
||||||
const oldValue = renderContext[key]
|
|
||||||
if (isRef(oldValue) && !isRef(value)) {
|
|
||||||
oldValue.value = value
|
|
||||||
} else {
|
|
||||||
renderContext[key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (key[0] === '$' && key.slice(1) in target) {
|
} else if (key[0] === '$' && key.slice(1) in target) {
|
||||||
__DEV__ &&
|
__DEV__ &&
|
||||||
warn(
|
warn(
|
||||||
|
@ -4,7 +4,6 @@ import {
|
|||||||
defineComponent,
|
defineComponent,
|
||||||
PropType,
|
PropType,
|
||||||
ref,
|
ref,
|
||||||
Ref,
|
|
||||||
reactive,
|
reactive,
|
||||||
createApp
|
createApp
|
||||||
} from './index'
|
} from './index'
|
||||||
@ -65,15 +64,12 @@ describe('with object props', () => {
|
|||||||
// setup context
|
// setup context
|
||||||
return {
|
return {
|
||||||
c: ref(1),
|
c: ref(1),
|
||||||
d: reactive({
|
d: {
|
||||||
e: ref('hi')
|
e: ref('hi')
|
||||||
}),
|
},
|
||||||
f: reactive({
|
f: reactive({
|
||||||
g: ref('hello' as GT)
|
g: ref('hello' as GT)
|
||||||
}),
|
})
|
||||||
h: {
|
|
||||||
i: ref('hi')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
@ -106,9 +102,6 @@ describe('with object props', () => {
|
|||||||
expectType<string>(this.d.e)
|
expectType<string>(this.d.e)
|
||||||
expectType<GT>(this.f.g)
|
expectType<GT>(this.f.g)
|
||||||
|
|
||||||
// should not unwrap refs nested under non-reactive objects
|
|
||||||
expectType<Ref<string>>(this.h.i)
|
|
||||||
|
|
||||||
// setup context properties should be mutable
|
// setup context properties should be mutable
|
||||||
this.c = 2
|
this.c = 2
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user