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
Vue 2: Vue.extend
proxy.$el in Vue 2 restricts to one child element. When you append a child to a parentNode, 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
Vue 3: createApp
proxy.$el in Vue 3, has HTMLElement (single child) or Text (multiple children). Would appending proxy.$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
  }
}
appendChild
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" />`
}
HelloWorld.vue