Search code examples
vue.jsvuejs3migration

Vue 3 Render Function: App Mounts Inside Root Element via innerHTML Instead of Replacing It


How do I do it on Vue 3 so that the root element is replaced by the rendered app instead of appending it inside the root element?

I am currently migrating our vue 2 to vue 3. I know that I can just deal with the root element being rendered but the app is a bit large and a lot are dependent on the App being rendered without the root element. So maybe there is a workaround to keep the expected behaviour or simulate the vue 2.x behavior?

How I render

import { createApp, h } from 'vue';
import ContentApp from '@js/ContentApp';

const app = createApp({
  render: () => h(ContentApp, { ... }),
});
app.use(store);
app.mount('#vueContent');
<!-- In DOM, the App renders on innerHtml of the root element (#vueContent) -->
<body>
    <div class="layout-base">
        <div id="vueContent">
            <!-- APP -->
            <div class="content-wrapper">
                <!-- content -->
            </div>
            <!-- APP -->
        </div>
    </div>
</body>

What I expect

<!-- In DOM, I expect that the App will replace the root element ('#vueContent')-->
<body>
    <div class="layout-base">
        <!-- APP -->
        <div class="content-wrapper">
            <!-- content -->
        </div>
        <!-- APP -->
    </div>
</body>

Solution

  • I found a way as a workaround to mimic the behaviour of vue 2.x app mounting by using fragments.

    import { createApp, h } from 'vue';
    import ContentApp from '@js/ContentApp';
    
    const fragment = document.createDocumentFragment();
    const targetEl = document.getElementById('vueContent');
    
    const app = createApp({
      render: () => h(ContentApp, { ... }),
    });
    app.use(store);
    // Temporarily mount the vue app on the fragment
    app.mount(fragment);
    
    // Now replace the target element with the fragment that has the app
    targetEl.parentNode.replaceChild(fragment, targetEl);