Vue 2.7.15
Vue 3.3.4
Vite 4.4.5
Node 16.14.0
NPM 8.3.1
`Vue.extend` to `createApp`
In Vue 2 if you want to extend a component Types.vue
import Vue from 'vue'
import Types from './Types.vue'
const methods = {
extendedTypes({ type, title, message }) {
const ExtendedVue = Vue.extend(Types)
const vm = new ExtendedVue({
propsData: {
type,
title,
message,
},
}).$mount()
return vm
}
}
// vm (aka Proxy Object)
const proxy = methods.extendedTyes('default', 'Hello', 'World')
const node = proxy.$el
proxy.$el
in Vue 2 restricts to one child element. When you append a child to aparentNode
, would be easy:parentNode.appendChild(proxy.$el)
to Vue 3
import { createApp } from 'vue'
import Types from './Types.vue'
const extend = (
vueOjbject = { template: `<span>test</span>` },
props = {}
) => {
const template = document.createElement('template')
const app = createApp(vueOjbject, props)
return app.mount(template) // Proxy Object
}
const methods = {
extendedTypes({ type, title, message }) {
return extend(Types, { type, title, message })
}
}
// vm (aka Proxy Object)
const proxy = methods.extendedTyes('default', 'Hello', 'World')
const node = proxy.$el
proxy.$el
in Vue 3, hasHTMLElement
(single child) orText
(multiple children). Would appendingproxy.$el
more challenging.
const appendChild = (parentNode, childNode) => {
console.log(`-------# constructor: ${childNode?.constructor?.name}`)
switch (true) {
case childNode instanceof HTMLTemplateElement:
console.log('---- HTMLTemplateElement')
Array.from(childNode.childNodes).forEach(child =>
parentNode.appendChild(child)
)
break
case childNode instanceof Text:
console.log('---- Text: Proxy.$el')
appendChild(parentNode, childNode.parentNode)
break
case childNode instanceof HTMLElement:
console.log('---- HTMLElement')
parentNode.appendChild(childNode)
break
default:
console.log('---- nothing')
break
}
}
const parentNode = document.createElement('div')
// Add Extended Vue Module to parentNode
appendChild(parentNode, proxy.$el)
Advanced mounting
Use'vue/dist/vue.esm-bundler'
instead of'vue'
import { defineComponent, createApp, reactive } from 'vue/dist/vue.esm-bundler'
const template = document.createElement('template')
const component = defineComponent({
template: `<h1 v-text="title"/>
<p v-text="description" />
<button @click="onClick">{{ buttonName }}</button>`,
props: ['title']},
data: () => ({
buttonName: 'Click,
description: ''
}),
methods: {
onClick() {
// Use `proxy` aka `vm`
if (typeof this.clicked === 'function') this.clicked(this)
}
}
)
const app = createApp(component, { title: 'Master of Code' })
// kind of `vm` instance after `mount`
const proxy = app.mount(template)
// controll the Vue Module
// --- attach `clicked` function
proxy.clicked = vm => {
vm.buttonName = 'Clicked'
alert('Button is clicked')
}
// appendChild to a element `parentNode`
const vInjectElements = () => {
const log = console.log
let logText = '/root'
const appendChild = (parentNode, childNode) => {
log(`-- # constructor: ${childNode?.constructor?.name}`)
logText += '.constructor'
let template
switch (true) {
case childNode instanceof HTMLTemplateElement:
log('---- HTMLTemplateElement')
logText += '.HTMLTemplateElement'
log(logText)
Array.from(childNode.childNodes).forEach(child =>
parentNode.appendChild(child)
)
break
case childNode instanceof Text:
log('---- Text: Proxy.$el')
logText += '.Text'
// template Element from childNode.parentNode
appendChild(parentNode, childNode.parentNode)
break
case childNode instanceof DocumentFragment:
log('---- DocumentFragment')
logText += '.DocumentFragment'
log(logText)
parentNode.appendChild(childNode)
break
case childNode instanceof HTMLElement:
log('---- HTMLElement')
logText += '.HTMLElement'
log(logText)
parentNode.appendChild(childNode)
break
case childNode instanceof Object:
log('---- Object Vue Component')
template = document.createElement('template')
logText += '.VueComponent'
// Check if object is a Vue App or Vue Component
if (typeof childNode.mount === 'function') {
// Vue App
log('------ by: createApp')
logText += '.createApp'
if (childNode._container) {
log('-------- is already mounted')
logText += '._container'
template = childNode._container
} else {
log('-------- mount the childNode')
logText += '.mountChildNodeToTemplate'
childNode.mount(template)
}
} else {
// Vue Component
log('------ by: defineComponent')
logText += '.defineComponent'
if (childNode?.$?.isMounted) {
logText += '.$el'
template = childNode.$el
} else {
logText += '.createAppAndMount'
createApp(childNode).mount(template)
}
}
appendChild(parentNode, template)
break
default:
log('---- nothing')
logText += '.nothing'
log(logText)
break
}
}
return {
mounted(el, binding) {
const { value: elements } = binding
elements.forEach(childNode => appendChild(el, childNode))
}
}
}
export default {
directives: {
injectElements: vInjectElements
},
data: () => ({
list: []
}),
template: `<h1>Title</h1>
<p>Some paragraph here</p>
<section v-inject-elements="list" />`
}