fix(custom-elements): fix event emitting for async custom elements (#5601)

fix #5599
This commit is contained in:
Thorsten Lünborg 2022-11-11 05:33:17 +01:00 committed by GitHub
parent 0b39e46192
commit 665f2ae121
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 32 additions and 20 deletions

View File

@ -211,13 +211,16 @@ export function defineAsyncComponent<
function createInnerComp(
comp: ConcreteComponent,
{
vnode: { ref, props, children, shapeFlag },
parent
}: ComponentInternalInstance
parent: ComponentInternalInstance
) {
const { ref, props, children, ce } = parent.vnode
const vnode = createVNode(comp, props, children)
// ensure inner component inherits the async wrapper's ref owner
vnode.ref = ref
// pass the custom element callback on to the inner comp
// and remove it from the async wrapper
vnode.ce = ce
delete parent.vnode.ce
return vnode
}

View File

@ -136,14 +136,6 @@ function reload(id: string, newComp: HMRComponent) {
// components to be unmounted and re-mounted. Queue the update so that we
// don't end up forcing the same parent to re-render multiple times.
queueJob(instance.parent.update)
// instance is the inner component of an async custom element
// invoke to reset styles
if (
(instance.parent.type as ComponentOptions).__asyncLoader &&
instance.parent.ceReload
) {
instance.parent.ceReload((newComp as any).styles)
}
} else if (instance.appContext.reload) {
// root instance mounted via createApp() has a reload method
instance.appContext.reload()

View File

@ -1,5 +1,6 @@
import {
defineAsyncComponent,
defineComponent,
defineCustomElement,
h,
inject,
@ -227,7 +228,7 @@ describe('defineCustomElement', () => {
})
describe('emits', () => {
const E = defineCustomElement({
const CompDef = defineComponent({
setup(_, { emit }) {
emit('created')
return () =>
@ -241,6 +242,7 @@ describe('defineCustomElement', () => {
})
}
})
const E = defineCustomElement(CompDef)
customElements.define('my-el-emits', E)
test('emit on connect', () => {
@ -277,6 +279,26 @@ describe('defineCustomElement', () => {
expect(spy1).toHaveBeenCalledTimes(1)
expect(spy2).toHaveBeenCalledTimes(1)
})
test('emit from within async component wrapper', async () => {
const E = defineCustomElement(
defineAsyncComponent(
() => new Promise<typeof CompDef>(res => res(CompDef as any))
)
)
customElements.define('my-async-el-emits', E)
container.innerHTML = `<my-async-el-emits></my-async-el-emits>`
const e = container.childNodes[0] as VueElement
const spy = jest.fn()
e.addEventListener('my-click', spy)
// this feels brittle but seems necessary to reach the node in the DOM.
await customElements.whenDefined('my-async-el-emits')
e.shadowRoot!.childNodes[0].dispatchEvent(new CustomEvent('click'))
expect(spy).toHaveBeenCalled()
expect(spy.mock.calls[0][0]).toMatchObject({
detail: [1]
})
})
})
describe('slots', () => {

View File

@ -341,15 +341,10 @@ export class VueElement extends BaseClass {
this._styles.length = 0
}
this._applyStyles(newStyles)
// if this is an async component, ceReload is called from the inner
// component so no need to reload the async wrapper
if (!(this._def as ComponentOptions).__asyncLoader) {
// reload
this._instance = null
this._update()
}
}
}
const dispatch = (event: string, args: any[]) => {
this.dispatchEvent(