fix(runtime-core): align $parent/$root with the template ref when using expose (#3158)

This commit is contained in:
HcySunYang 2021-02-07 21:39:52 +08:00 committed by GitHub
parent 3efa2aff13
commit f43a3b0beb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 6 deletions

View File

@ -141,4 +141,32 @@ describe('api: expose', () => {
expect(childRef.value).toBeTruthy() expect(childRef.value).toBeTruthy()
expect(childRef.value.foo).toBe(1) expect(childRef.value.foo).toBe(1)
}) })
test('with $parent/$root', () => {
const Child = defineComponent({
render() {
expect((this.$parent! as any).foo).toBe(1)
expect((this.$parent! as any).bar).toBe(undefined)
expect((this.$root! as any).foo).toBe(1)
expect((this.$root! as any).bar).toBe(undefined)
}
})
const Parent = defineComponent({
expose: [],
setup(_, { expose }) {
expose({
foo: 1
})
return {
bar: 2
}
},
render() {
return h(Child)
}
})
const root = nodeOps.createElement('div')
render(h(Parent), root)
})
}) })

View File

@ -510,6 +510,10 @@ export function validateComponentName(name: string, config: AppConfig) {
} }
} }
export function isStatefulComponent(instance: ComponentInternalInstance) {
return instance.vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
}
export let isInSSRComponentSetup = false export let isInSSRComponentSetup = false
export function setupComponent( export function setupComponent(
@ -518,8 +522,8 @@ export function setupComponent(
) { ) {
isInSSRComponentSetup = isSSR isInSSRComponentSetup = isSSR
const { props, children, shapeFlag } = instance.vnode const { props, children } = instance.vnode
const isStateful = shapeFlag & ShapeFlags.STATEFUL_COMPONENT const isStateful = isStatefulComponent(instance)
initProps(instance, props, isStateful, isSSR) initProps(instance, props, isStateful, isSSR)
initSlots(instance, children) initSlots(instance, children)

View File

@ -1,4 +1,8 @@
import { ComponentInternalInstance, Data } from './component' import {
ComponentInternalInstance,
Data,
isStatefulComponent
} from './component'
import { nextTick, queueJob } from './scheduler' import { nextTick, queueJob } from './scheduler'
import { instanceWatch, WatchOptions, WatchStopHandle } from './apiWatch' import { instanceWatch, WatchOptions, WatchStopHandle } from './apiWatch'
import { import {
@ -207,8 +211,11 @@ type PublicPropertiesMap = Record<string, (i: ComponentInternalInstance) => any>
*/ */
const getPublicInstance = ( const getPublicInstance = (
i: ComponentInternalInstance | null i: ComponentInternalInstance | null
): ComponentPublicInstance | null => ): ComponentPublicInstance | ComponentInternalInstance['exposed'] | null => {
i && (i.proxy ? i.proxy : getPublicInstance(i.parent)) if (!i) return null
if (isStatefulComponent(i)) return i.exposed ? i.exposed : i.proxy
return getPublicInstance(i.parent)
}
const publicPropertiesMap: PublicPropertiesMap = extend(Object.create(null), { const publicPropertiesMap: PublicPropertiesMap = extend(Object.create(null), {
$: i => i, $: i => i,
@ -219,7 +226,7 @@ const publicPropertiesMap: PublicPropertiesMap = extend(Object.create(null), {
$slots: i => (__DEV__ ? shallowReadonly(i.slots) : i.slots), $slots: i => (__DEV__ ? shallowReadonly(i.slots) : i.slots),
$refs: i => (__DEV__ ? shallowReadonly(i.refs) : i.refs), $refs: i => (__DEV__ ? shallowReadonly(i.refs) : i.refs),
$parent: i => getPublicInstance(i.parent), $parent: i => getPublicInstance(i.parent),
$root: i => i.root && i.root.proxy, $root: i => getPublicInstance(i.root),
$emit: i => i.emit, $emit: i => i.emit,
$options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type), $options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type),
$forceUpdate: i => () => queueJob(i.update), $forceUpdate: i => () => queueJob(i.update),