Option 1: createVNode(component, props)
and render(vnode, container)
Creating: Use createVNode()
to create a VNode
of a component definition (e.g., imported SFC from *.vue
) with props, which could be passed to render()
to render it on a given container element.
Destroying: Calling render(null, container)
destroys the VNode
attached to the container. This should be called as cleanup when the parent component unmounts (via unmounted
lifecycle hook).
// renderComponent.js
import { createVNode, render } from 'vue'
export default function renderComponent({ el, component, props, appContext }) {
let vnode = createVNode(component, props)
vnode.appContext = { ...appContext }
render(vnode, el)
return () => {
// destroy vnode
render(null, el)
vnode = undefined
}
}
Caveat: This approach relies on internal methods (createVNode
and render
), which could be refactored or removed in a future release.
demo 1
Option 2: createApp(component, props)
and app.mount(container)
Creating: Use createApp()
, which returns an application instance. The instance has mount()
, which can be used to render the component on a given container element.
Destroying: The application instance has unmount()
to destroy the app and component instances. This should be called as cleanup when the parent component unmounts (via unmounted
lifecycle hook).
// renderComponent.js
import { createApp } from 'vue'
export default function renderComponent({ el, component, props, appContext }) {
let app = createApp(component, props)
Object.assign(app._context, appContext) // must use Object.assign on _context
app.mount(el)
return () => {
// destroy app/component
app?.unmount()
app = undefined
}
}
Caveat: This approach creates an application instance for each component, which could be non-trivial overhead if there's a need to instantiate many components simultaneously in the document.
demo 2
Example usage
<script setup>
import { ref, onUnmounted, getCurrentInstance } from 'vue'
import renderComponent from './renderComponent'
const { appContext } = getCurrentInstance()
const container = ref()
let counter = 1
let destroyComp = null
onUnmounted(() => destroyComp?.())
const insert = async () => {
destroyComp?.()
destroyComp = renderComponent({
el: container.value,
component: (await import('@/components/HelloWorld.vue')).default
props: {
key: counter,
msg: 'Message ' + counter++,
},
appContext,
})
}
</script>
<template>
<button @click="insert">Insert component</button>
<div ref="container"></div>
</template>