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( function createInnerComp(
comp: ConcreteComponent, comp: ConcreteComponent,
{ parent: ComponentInternalInstance
vnode: { ref, props, children, shapeFlag },
parent
}: ComponentInternalInstance
) { ) {
const { ref, props, children, ce } = parent.vnode
const vnode = createVNode(comp, props, children) const vnode = createVNode(comp, props, children)
// ensure inner component inherits the async wrapper's ref owner // ensure inner component inherits the async wrapper's ref owner
vnode.ref = ref 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 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 // 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. // don't end up forcing the same parent to re-render multiple times.
queueJob(instance.parent.update) 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) { } else if (instance.appContext.reload) {
// root instance mounted via createApp() has a reload method // root instance mounted via createApp() has a reload method
instance.appContext.reload() instance.appContext.reload()

View File

@ -1,5 +1,6 @@
import { import {
defineAsyncComponent, defineAsyncComponent,
defineComponent,
defineCustomElement, defineCustomElement,
h, h,
inject, inject,
@ -227,7 +228,7 @@ describe('defineCustomElement', () => {
}) })
describe('emits', () => { describe('emits', () => {
const E = defineCustomElement({ const CompDef = defineComponent({
setup(_, { emit }) { setup(_, { emit }) {
emit('created') emit('created')
return () => return () =>
@ -241,6 +242,7 @@ describe('defineCustomElement', () => {
}) })
} }
}) })
const E = defineCustomElement(CompDef)
customElements.define('my-el-emits', E) customElements.define('my-el-emits', E)
test('emit on connect', () => { test('emit on connect', () => {
@ -277,6 +279,26 @@ describe('defineCustomElement', () => {
expect(spy1).toHaveBeenCalledTimes(1) expect(spy1).toHaveBeenCalledTimes(1)
expect(spy2).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', () => { describe('slots', () => {

View File

@ -341,15 +341,10 @@ export class VueElement extends BaseClass {
this._styles.length = 0 this._styles.length = 0
} }
this._applyStyles(newStyles) 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._instance = null
this._update() this._update()
} }
} }
}
const dispatch = (event: string, args: any[]) => { const dispatch = (event: string, args: any[]) => {
this.dispatchEvent( this.dispatchEvent(