Search code examples
vue.jspluginselectroncomponents

Load vue component (truly) dynamically from local file


Is it possible to load a vue component dynamically at runtime (in an electron app to build a plugin system)?

  • The component is in a specific file
  • Its path is only known at runtime
  • The component can either be precompiled (if that is possible, don't know) or is compiled at runtime
  • A simple example component is listed below

I tried the following approaches, both failing:

  1. Require component

    <template>
      <component :is="currentComp"></component>
    </template>
    
    <script>
    ...
    methods: {
      loadComponent(path) {
        const dynComp = eval('require(path)'); // use eval to prevent webpackresolving the require
        this.currentComp = dynComp;
      }
    },
    ...
    </script>
    

    The import works, but the line this.currentComp = dynComp; Fails with error message:

    Error in data(): "Error: An object could not be cloned."

  2. Using the code presented here, but replace url with a local path

    Fails with error message:

    Failed to resolve async component: function MyComponent() {
      return externalComponent('/path/to/Component.vue');
    }
    Reason: TypeError: Chaining cycle detected for promise #<Promise>
    

The used example component is the following:

// Example component
module.exports = {
  template: `
  <div>
    <input v-model="value"/>
    <button @click="clear">Clear</button>
    <div>{{ value }}</div>
  </div>`,
  name: 'Example',
  data() {
    return {
      value: '',
    };
  },
  watch: {
    value(value) {
      console.log('change!');
    },
  },
  methods: {
    clear() {
      this.value = '';
    },
  },
};


Solution

  • I found a solution:

    1. Create the vue component as a SFC in a separate file (here src/Component.vue). I didn't try, but probably it works for inline components, too.

    2. Precompile the component using vue-cli-service, which is already a dev dependency, if the project is created using vue-cli (It's nice to use vue-cli here, since the required loaders are already included):

      yarn vue-cli-service build --target lib src/Command.vue
      

      The component is compiled to different bundle types in the dist directory. The file [filename].umd.min.js can be imported now.

    3. Import the component dynamically at runtime:

      let MyComponent = eval(`require('/absolute/path/to/[filename].umd.min.js')`);
      Vue.component('MyComponent', MyComponent);
      

      The require is wrapped inside an eval to prevent webpack of trying to include the import in its bundle and transforming the require into a webpack__require.

    4. (Optional) If the SFC component contains a <style>...</style> tag, the resulting css is compiled to a separate file. The css can be inlined in the js file by adding the following lines to the vue.config.js in the components project root:

      module.exports = {
        ...
        css: {
          extract: false,
        },
      };