feat(expose): always expose $ instance properties on child refs
This commit is contained in:
parent
a5a66c5196
commit
b0203a3092
@ -7,7 +7,7 @@ describe('api: expose', () => {
|
||||
render() {},
|
||||
setup(_, { expose }) {
|
||||
expose({
|
||||
foo: ref(1),
|
||||
foo: 1,
|
||||
bar: ref(2)
|
||||
})
|
||||
return {
|
||||
@ -169,4 +169,26 @@ describe('api: expose', () => {
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(Parent), root)
|
||||
})
|
||||
|
||||
test('expose should allow access to built-in instance properties', () => {
|
||||
const Child = defineComponent({
|
||||
render() {
|
||||
return h('div')
|
||||
},
|
||||
setup(_, { expose }) {
|
||||
expose()
|
||||
return {}
|
||||
}
|
||||
})
|
||||
|
||||
const childRef = ref()
|
||||
const Parent = {
|
||||
setup() {
|
||||
return () => h(Child, { ref: childRef })
|
||||
}
|
||||
}
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(Parent), root)
|
||||
expect(childRef.value.$el.tag).toBe('div')
|
||||
})
|
||||
})
|
||||
|
@ -14,7 +14,8 @@ import {
|
||||
createRenderContext,
|
||||
exposePropsOnRenderContext,
|
||||
exposeSetupStateOnRenderContext,
|
||||
ComponentPublicInstanceConstructor
|
||||
ComponentPublicInstanceConstructor,
|
||||
publicPropertiesMap
|
||||
} from './componentPublicInstance'
|
||||
import {
|
||||
ComponentPropsOptions,
|
||||
@ -169,7 +170,7 @@ export interface SetupContext<E = EmitsOptions> {
|
||||
attrs: Data
|
||||
slots: Slots
|
||||
emit: EmitFn<E>
|
||||
expose: (exposed: Record<string, any>) => void
|
||||
expose: (exposed?: Record<string, any>) => void
|
||||
}
|
||||
|
||||
/**
|
||||
@ -291,6 +292,7 @@ export interface ComponentInternalInstance {
|
||||
|
||||
// exposed properties via expose()
|
||||
exposed: Record<string, any> | null
|
||||
exposeProxy: Record<string, any> | null
|
||||
|
||||
/**
|
||||
* alternative proxy used only for runtime-compiled render functions using
|
||||
@ -447,6 +449,7 @@ export function createComponentInstance(
|
||||
render: null,
|
||||
proxy: null,
|
||||
exposed: null,
|
||||
exposeProxy: null,
|
||||
withProxy: null,
|
||||
effects: null,
|
||||
provides: parent ? parent.provides : Object.create(appContext.provides),
|
||||
@ -837,7 +840,7 @@ export function createSetupContext(
|
||||
if (__DEV__ && instance.exposed) {
|
||||
warn(`expose() should be called only once per setup().`)
|
||||
}
|
||||
instance.exposed = proxyRefs(exposed)
|
||||
instance.exposed = exposed || {}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
@ -868,6 +871,23 @@ export function createSetupContext(
|
||||
}
|
||||
}
|
||||
|
||||
export function getExposeProxy(instance: ComponentInternalInstance) {
|
||||
if (instance.exposed) {
|
||||
return (
|
||||
instance.exposeProxy ||
|
||||
(instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), {
|
||||
get(target, key: string) {
|
||||
if (key in target) {
|
||||
return target[key]
|
||||
} else if (key in publicPropertiesMap) {
|
||||
return publicPropertiesMap[key](instance)
|
||||
}
|
||||
}
|
||||
}))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// record effects created during a component's setup() so that they can be
|
||||
// stopped when the component unmounts
|
||||
export function recordInstanceBoundEffect(
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
isString,
|
||||
isObject,
|
||||
isArray,
|
||||
EMPTY_OBJ,
|
||||
NOOP,
|
||||
isPromise
|
||||
} from '@vue/shared'
|
||||
@ -45,9 +44,7 @@ import {
|
||||
import {
|
||||
reactive,
|
||||
ComputedGetter,
|
||||
WritableComputedOptions,
|
||||
proxyRefs,
|
||||
toRef
|
||||
WritableComputedOptions
|
||||
} from '@vue/reactivity'
|
||||
import {
|
||||
ComponentObjectPropsOptions,
|
||||
@ -540,7 +537,7 @@ export let shouldCacheAccess = true
|
||||
|
||||
export function applyOptions(instance: ComponentInternalInstance) {
|
||||
const options = resolveMergedOptions(instance)
|
||||
const publicThis = instance.proxy!
|
||||
const publicThis = instance.proxy! as any
|
||||
const ctx = instance.ctx
|
||||
|
||||
// do not cache property access on public proxy during state initialization
|
||||
@ -773,12 +770,15 @@ export function applyOptions(instance: ComponentInternalInstance) {
|
||||
|
||||
if (isArray(expose)) {
|
||||
if (expose.length) {
|
||||
const exposed = instance.exposed || (instance.exposed = proxyRefs({}))
|
||||
const exposed = instance.exposed || (instance.exposed = {})
|
||||
expose.forEach(key => {
|
||||
exposed[key] = toRef(publicThis, key as any)
|
||||
Object.defineProperty(exposed, key, {
|
||||
get: () => publicThis[key],
|
||||
set: val => (publicThis[key] = val)
|
||||
})
|
||||
})
|
||||
} else if (!instance.exposed) {
|
||||
instance.exposed = EMPTY_OBJ
|
||||
instance.exposed = {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,7 +221,9 @@ const getPublicInstance = (
|
||||
return getPublicInstance(i.parent)
|
||||
}
|
||||
|
||||
const publicPropertiesMap: PublicPropertiesMap = extend(Object.create(null), {
|
||||
export const publicPropertiesMap: PublicPropertiesMap = extend(
|
||||
Object.create(null),
|
||||
{
|
||||
$: i => i,
|
||||
$el: i => i.vnode.el,
|
||||
$data: i => i.data,
|
||||
@ -236,7 +238,8 @@ const publicPropertiesMap: PublicPropertiesMap = extend(Object.create(null), {
|
||||
$forceUpdate: i => () => queueJob(i.update),
|
||||
$nextTick: i => nextTick.bind(i.proxy!),
|
||||
$watch: i => (__FEATURE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP)
|
||||
} as PublicPropertiesMap)
|
||||
} as PublicPropertiesMap
|
||||
)
|
||||
|
||||
if (__COMPAT__) {
|
||||
installCompatInstanceProperties(publicPropertiesMap)
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
ComponentOptions,
|
||||
createComponentInstance,
|
||||
Data,
|
||||
getExposeProxy,
|
||||
setupComponent
|
||||
} from './component'
|
||||
import {
|
||||
@ -335,7 +336,7 @@ export const setRef = (
|
||||
|
||||
const refValue =
|
||||
vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
|
||||
? vnode.component!.exposed || vnode.component!.proxy
|
||||
? getExposeProxy(vnode.component!) || vnode.component!.proxy
|
||||
: vnode.el
|
||||
const value = isUnmount ? null : refValue
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user