Skip to content

Commit f4f8dba

Browse files
committed
fix(define-custom-element): ensure async wrapper passes custom element callback to inner component
This ensures event emits work for async coponent in custom elements.
1 parent 5898629 commit f4f8dba

File tree

4 files changed

+34
-17
lines changed

4 files changed

+34
-17
lines changed

packages/runtime-core/src/apiAsyncComponent.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,16 @@ export function defineAsyncComponent<
211211

212212
function createInnerComp(
213213
comp: ConcreteComponent,
214-
{ vnode: { ref, props, children } }: ComponentInternalInstance
214+
parent: ComponentInternalInstance
215215
) {
216+
const { ref, props, children, ce } = parent.vnode
216217
const vnode = createVNode(comp, props, children)
217218
// ensure inner component inherits the async wrapper's ref owner
218219
vnode.ref = ref
220+
// pass the custom element callback on to the inner comp
221+
// and remove it from the async wrapper
222+
vnode.ce = ce
223+
delete parent.vnode.ce
224+
219225
return vnode
220226
}

packages/runtime-core/src/hmr.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,6 @@ function reload(id: string, newComp: HMRComponent) {
136136
// components to be unmounted and re-mounted. Queue the update so that we
137137
// don't end up forcing the same parent to re-render multiple times.
138138
queueJob(instance.parent.update)
139-
// instance is the inner component of an async custom element
140-
// invoke to reset styles
141-
if (
142-
(instance.parent.type as ComponentOptions).__asyncLoader &&
143-
instance.parent.ceReload
144-
) {
145-
instance.parent.ceReload((newComp as any).styles)
146-
}
147139
} else if (instance.appContext.reload) {
148140
// root instance mounted via createApp() has a reload method
149141
instance.appContext.reload()

packages/runtime-dom/__tests__/customElement.spec.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
defineAsyncComponent,
3+
defineComponent,
34
defineCustomElement,
45
h,
56
inject,
@@ -213,7 +214,7 @@ describe('defineCustomElement', () => {
213214
})
214215

215216
describe('emits', () => {
216-
const E = defineCustomElement({
217+
const CompDef = defineComponent({
217218
setup(_, { emit }) {
218219
emit('created')
219220
return () =>
@@ -222,6 +223,7 @@ describe('defineCustomElement', () => {
222223
})
223224
}
224225
})
226+
const E = defineCustomElement(CompDef)
225227
customElements.define('my-el-emits', E)
226228

227229
test('emit on connect', () => {
@@ -243,6 +245,28 @@ describe('defineCustomElement', () => {
243245
detail: [1]
244246
})
245247
})
248+
249+
test('emit from within async component wrapper', async () => {
250+
const E = defineCustomElement(
251+
defineAsyncComponent(
252+
() => new Promise<typeof CompDef>(res => res(CompDef as any))
253+
)
254+
)
255+
customElements.define('my-async-el-emits', E)
256+
container.innerHTML = `<my-async-el-emits></my-async-el-emits>`
257+
const e = container.childNodes[0] as VueElement
258+
const spy = jest.fn()
259+
e.addEventListener('my-click', spy)
260+
// this feels brittle but seems necessary to reach the node in the DOM.
261+
await nextTick()
262+
await nextTick()
263+
await nextTick()
264+
e.shadowRoot!.childNodes[0].dispatchEvent(new CustomEvent('click'))
265+
expect(spy).toHaveBeenCalled()
266+
expect(spy.mock.calls[0][0]).toMatchObject({
267+
detail: [1]
268+
})
269+
})
246270
})
247271

248272
describe('slots', () => {

packages/runtime-dom/src/apiCustomElement.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -327,13 +327,8 @@ export class VueElement extends BaseClass {
327327
this._styles.length = 0
328328
}
329329
this._applyStyles(newStyles)
330-
// if this is an async component, ceReload is called from the inner
331-
// component so no need to reload the async wrapper
332-
if (!(this._def as ComponentOptions).__asyncLoader) {
333-
// reload
334-
this._instance = null
335-
this._update()
336-
}
330+
this._instance = null
331+
this._update()
337332
}
338333
}
339334

0 commit comments

Comments
 (0)