Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

defineCustomElement looses provide context configured by configureApp on HMR #12453

Open
danieldasilva-smake opened this issue Nov 21, 2024 · 0 comments · May be fixed by #12455
Open

defineCustomElement looses provide context configured by configureApp on HMR #12453

danieldasilva-smake opened this issue Nov 21, 2024 · 0 comments · May be fixed by #12455
Labels
🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. scope: custom elements scope: hmr

Comments

@danieldasilva-smake
Copy link

danieldasilva-smake commented Nov 21, 2024

Vue version

3.5.12

Link to minimal reproduction

https://stackblitz.com/edit/vitejs-vite-kennhm?file=src%2FApp.ce.vue

Steps to reproduce

  1. Open the reproduction link
  2. Remove the comment in App.ce.vue after the app is running

If you create a custom element with defineCustomElement and provide something in the app via configureApp the provided context gets lost when a HMR occurs.

What is expected?

The provide context should be kept

What is actually happening?

The provide context will be lost

System Info

No response

Any additional comments?

This is happening because when the custom element will be mounted a VNode will be created that will be linked to the app via _ceVNode (see

this._app._ceVNode = this._createVNode()
). But when a HMR occurs a new VNode will be created (see
render(this._createVNode(), this._root)
) which isn't linked to the app anymore and doesn't have its context.

A possible solution would be to link the newly created VNode with the app or to not create a new VNode on every HMR.
Here is a snippet of a custom definition of defineCustomElement which I did which worked for me:

const vnode = createVNode(AppComponent, this._props);
// @ts-expect-error: Isn't typed
vnode.ce = (instance: ComponentInternalInstance) => {
    // @ts-expect-error: Isn't typed
    instance.ceReload = (newStyles: string[] | undefined) => {
        styleElements.forEach((styleElement) => {
            this._shadowRoot?.removeChild(styleElement);
        });
        styleElements =
            newStyles?.map((style) => {
                const styleElement = document.createElement("style");
                styleElement.textContent = style;
                this._shadowRoot?.appendChild(styleElement);
                return styleElement;
            }) ?? [];
    };
    instance.emit = (event: string, ...args: unknown[]) => {
        this.dispatchEvent(new CustomEvent(event, { detail: args }));
    };
};
this._app = createApp(vnode);
this._def.configureApp(this._app);
this._app.mount(rootContainer);

There you first create a VNode with the component and then create the app with the vnode. This way you can set the ce property on the VNode and don't have to create one on every HMR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. scope: custom elements scope: hmr
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants