Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load Vue single-file component dynamically at runtime

We've got a Vue app and would like to allow third parties to create plugins. We'd like the plugins to be built in the form of a Vue single-file component.

At runtime, the end user would select a plugin to add to the app. The app would fetch the plain-text .vue file, compile it on the fly, and display it in the app.

Vue supports dynamic and async components, but these to be compiled into the app ahead of time. We'd like to do the same thing, except load the code on the fly.

How can I make this work?

Here's what I've got so far:

<template>
  <div>
    The component goes here:
    <component :is="pluginComponent"></component>
  </div>
</template>
<script>
import { parseComponent } from "vue-template-compiler";
export default {
  data() {
    return {
      pluginComponent: null
    };
  },
  mounted() {
    // fetch code from some external source here
    let code = "<template><div>hello</div></template>";
    let comp = parseComponent(code);
    this.pluginComponent = comp;
  }
};
</script>

(I modified the build so the vue-template-compiler is present.)

The code above generates this error:

[Vue warn]: invalid template option:[object Object]
found in
---> <Anonymous>
       <Pages/plugin/PluginId.vue> at pages/plugin/_plugin_id.vue
         <Nuxt>
           <Layouts/default.vue> at layouts/default.vue
             <Root> instrument.js:110
    instrumentConsole instrument.js:110
    VueJS 15
TypeError: "vnode is null"
    VueJS 14
instrument.js:110

I'm guessing that whatever parseComponent() produces isn't what <component> is looking for.

like image 809
ccleve Avatar asked May 27 '20 03:05

ccleve


People also ask

How do I load a script into Vue component?

Add a script tag inside your Vue component template. Just add the script into the component template. But don't forget to add, type="application/javascript" to the script tag otherwise it will show an error during the build. However, if you have added document.

Is Vue synchronous or asynchronous?

You might know Vue updates reactively: when you change a value, the DOM is automatically updated to reflect the latest value. Vue does these updates asynchronously. In contrast, a test runner like Jest runs synchronously. This can cause some surprising results in tests.

How to dynamically load a Vue component?

Usually, if we want to dynamically load a Vue.js component, we use a dynamic import. This works great as long as MyComponent is part of the code base (either directly or as a npm dependency) of the Vue.js client application.

How does Vue work at runtime?

At runtime, the end user would select a plugin to add to the app. The app would fetch the plain-text .vue file, compile it on the fly, and display it in the app. Vue supports dynamic and async components, but these to be compiled into the app ahead of time.

How do you build plugins in Vue?

We'd like the plugins to be built in the form of a Vue single-file component. At runtime, the end user would select a plugin to add to the app. The app would fetch the plain-text .vue file, compile it on the fly, and display it in the app. Vue supports dynamic and async components, but these to be compiled into the app ahead of time.

Is it possible to use mycomponent with Vue?

This works great as long as MyComponent is part of the code base (either directly or as a npm dependency) of the Vue.js client application.


Video Answer


1 Answers

I'm guessing that whatever parseComponent() produces isn't what <component> is looking for

I'd say yes, as it doesn't seem to compile to any render functions.

As stated in the docs, vue-template-compiler is for runtime-compilation. And in most cases you should be using it with vue-loader.

How can I make this work?

You might want to use Vue.compile as it allows you to compile template string into a render function; which you can then bind to an object for an async or dynamic component.

Do note, however, that this is only available in the full build, which is roughly 30% heavier-weight than the runtime-only build counterparts. Read more on Runtime + Compiler vs. Runtime-only.

With that in mind, since you didn't mention in the question, which bundler you are using, I'm going to assume Webpack with Vue-CLI, and here's how you would configure the vue alias (as a reference point when importing).

Verifying your set Vue "alias"

In your console (from the root of the project), run:

vue inspect resolve.alias.vue$

If that results in something along "vue/dist/vue.runtime.esm.js" (it should be, by default), then it's clear that we need to change this part.

Configuring it

Now, since the internal webpack config is maintained using webpack-chain, we would configure/reset the alias like so:

module.exports = {
  chainWebpack: config => {
    config.resolve.alias
      .set('vue$', 'vue/dist/vue.esm.js')
  }
}

Check out the explanation of different builds.

Using it

And at this point, all you'd need to do is pass the "dynamic" template to the compile function, excluding the <template> tag though.

import Vue from 'vue';

export default {
  mounted() {
    // fetch code from some external source here
    let code = '<div>hello</div>';
    let comp = Vue.compile(code);
    
    this.pluginComponent = comp;
  }
}
<template>
  <div>
    The component goes here:
    <component :is="pluginComponent"></component>
  </div>
</template>
like image 104
Yom T. Avatar answered Oct 20 '22 04:10

Yom T.