-
-
Notifications
You must be signed in to change notification settings - Fork 8.9k
Description
Vue version
3.5.12
Link to minimal reproduction
https://stackblitz.com/edit/vitejs-vite-b9enev?file=src%2Fmain.js
Steps to reproduce
- Open the link to the reproduction
- Observe
app-injectednot displayed in the preview
What is expected?
When given an async component, defineCustomElement with the configureApp option results in configureApp being called on the loaded component's mount.
What is actually happening?
When given an async component, defineCustomElement with the configureApp option does not result in configureApp being called on the loaded component's mount.
System Info
System:
OS: Linux 5.15 Debian GNU/Linux 10 (buster) 10 (buster)
CPU: (8) x64 Intel(R) Core(TM) i7-10610U CPU @ 1.80GHz
Memory: 1.41 GB / 7.76 GB
Container: Yes
Shell: 5.0.3 - /bin/bash
Binaries:
Node: 16.20.2 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 8.19.4 - /usr/local/bin/npm
npmPackages:
vue: ^3.5.12 => 3.5.12Any additional comments?
When resolving an async component, the component's loader is called and the loaded component is resolved.
core/packages/runtime-dom/src/apiCustomElement.ts
Lines 404 to 411 in 06310e8
| const asyncDef = (this._def as ComponentOptions).__asyncLoader | |
| if (asyncDef) { | |
| this._pendingResolve = asyncDef().then(def => | |
| resolve((this._def = def), true), | |
| ) | |
| } else { | |
| resolve(this._def) | |
| } |
However, the configureApp function is attached to the async wrapper. When the component is loaded and the wrapper is discarded, configureApp is discarded as well. Thus when the component is mounted, configureApp is not called.
| this._mount(def) |
core/packages/runtime-dom/src/apiCustomElement.ts
Lines 414 to 424 in 06310e8
| private _mount(def: InnerComponentDef) { | |
| if ((__DEV__ || __FEATURE_PROD_DEVTOOLS__) && !def.name) { | |
| // @ts-expect-error | |
| def.name = 'VueElement' | |
| } | |
| this._app = this._createApp(def) | |
| if (def.configureApp) { | |
| def.configureApp(this._app) | |
| } | |
| this._app._ceVNode = this._createVNode() | |
| this._app.mount(this._root) |
A fix here may be to assign the function from the wrapper to the loaded component before reassignment of this._def.
const asyncDef = this._def.__asyncLoader;
if (asyncDef) {
this._pendingResolve = asyncDef().then(
(def) => {
if (this._def.configureApp) {
def.configureApp = this._def.configureApp;
}
resolve(this._def = def, true);
}
);
} else {
resolve(this._def);
}An interesting workaround is to move configureApp into the component's options as shown by this repro. I'm not sure if this is intentional or of the repercussions.