Search code examples
javascriptvue.jsvuejs2vue-componentvuejs3

How to programmatically create a component instance in Vue 3?


I have a Vue 2 pattern I was using for a common scenario: programmatically creating an instance to open a Modal/Dialog/Lightbox on dynamic content outside of a template.

In Vue 2, I found this pattern:

// DialogService.js

export default {
  alert(text) {
    const DialogClass = Vue.extend(DialogComponentDef);
    let dialog = new DialogClass({ propsData: { text } });

    dialog.$on('close', () => {
      dialog.$destroy();
      dialog.$el.remove();
      dialog = null;
    });

    // mount the dynamic dialog component in the page
    const mountEl = document.createElement('div');
    document.body.appendChild(mountEl);
    dialog.$mount(mountEl);
  },
};

How can I acheive this in Vue 3, knowing Vue.extends, $on & $destroy do not exist anymore? You can see a full example of the DialogService.js by clicking here.


Solution

  • Here's how to do with createApp in Vue 3, but the context (stores, plugins, directives...) will not be kept.

    // DialogService.js
    import { createApp } from 'vue';
    
    export default {
      alert(text) {
        const mountEl = document.createElement('div');
        document.body.appendChild(mountEl);
    
        const dialog = createApp({ extends: DialogComponentDef }, {
          // props
          text,
          // events are passed as props here with on[EventName]
          onClose() {
            mountEl.parentNode.removeChild(mountEl);
            dialog.unmount();
            dialog = null;
          },
        });
    
        dialog.mount(mountEl);
      },
    };
    

    To keep the context, there's something more complicated that can be seen here with h and render Vue methods : https://github.com/vuejs/vue-next/issues/2097#issuecomment-709860132