Search code examples
vuejs3mount

Cannont mount twice the same Vue 3 component


Im' mounting a Vue 3 component as a loading animation during a frame loading with :

createApp(App).mount(e);

When the frame is loaded, the component is erased in the html by the frame content (but not by Vue I guess). This behaviour is managed by an external system of Vue.

When I'm trying to reload the component with the same command, the component is not displayed.

If I only mount again the component, I have the following warning in the console and the component is not displayed :

"[Vue warn]: App has already been mounted.
If you want to remount the same app, move your app creation logic into a factory function and create fresh app instances for each mount - e.g. `const createMyApp = () => createApp(App)`".

I've found also a way to replicate the issue with :

const app = createApp(App)
app.mount(el);
el.innerHtml = '';
app.mount(el);

I also try to unmount the component with no more success :

const app = createApp(App);
app.mount(el);
app.unmount();
app.mount(el);

So what is the correct way to display again the same vue component when it is erased externally ?


Solution

  • An far more better option is to use Vue custom elements.

    First, create a classical SFC with ce.vue extension :

    //test.ce.vue
    <template>
        <div class="text-primary">Test</div>
    </template>
    
    <script>
    export default {
        name: 'test',
    };
    </script>
    
    <style>
        .text-primary {
            color: red;
        }
    
    </style>
    

    And then in the main script :

    //app.js
    import Test from 'test.ce.vue';
    const testElement = defineCustomElement(Test);
    customElements.define('test-element', testElement);
    
    document.body.appendChild(document.createElement('test-element'));
    

    The component will be rendered as soon as is detected in the document:

    <test-component>
        #shadow-root (open)
            <style>
                .text-primary {
                    color: red;
                }
            </style>
            <div class="text-primary">Test</div>
    </test-component>