I have a plugin written in Vue 2 that programmatically mounts a component onto existing app instance.
export default {
install (Vue) {
const component = new (Vue.extend(Component))()
const vm = component.$mount()
document.body.appendChild(vm.$el)
}
}
Trying to migrate this to Vue 3 and this is the closest I've got.
export default {
install (app) {
const component = defineComponent(Component)
const container = document.createElement('div')
document.body.appendChild(container)
const vm = createApp(component).mount(container)
}
}
The problem is that createApp creates a new app instance, while Vue.extend created only a subclass. I need to have all globally available components in the main app, available in plugin's component as well. Creating a new app instance prevents this.
Component needs to be programatically mounted. Manually inserting it into template is not an option.
Please help.
Interesting problem! There is a discussion on the Vue GitHub and they created a module that you can use: mount-vue-component. I found it very helpful to look at the module's code, it does exactly what you want to do.
To instantiate the component, you have to create a VNode, which you can do with createVNode()
(which is the same as h()
). To get access to the app's context, including global components, you have to set the appContext
property. Finally, the render()
function mounts the component instance to an HTML Element.
So for you example, that gives you:
export default {
install (app) {
const container = document.createElement('div')
document.body.appendChild(container)
const vNode = createVNode(component)
vNode.appContext = app._context
render(vNode, container)
}
}
Here it is in a snippet:
const { createApp, createVNode, ref, render } = Vue;
const App = {
data() {
return {
msg: ref('Hello from app!')
}
}
}
const app = createApp(App)
app.component('inner-component', {
template: '<div style="color: green;">This is a global component in app</div>'
})
app.mount('#app')
const component = {
props: ['color'],
template: '<span :style="{color: color}">Hello from mounted component!</span><inner-component></inner-component>'
}
const el = document.getElementById('leComponent')
const vNode = createVNode(component, {color: 'blue'}, [])
vNode.appContext = app._context
render(vNode, el)
div{
margin: 4px;
padding: 4px;
border: 1px solid #333;
}
<div id="app">
{{msg}}
<inner-component></inner-component>
</div>
<div id="leComponent"></div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>