Home > Back-end >  How to create a library exposing a single Vue component that can be consumed by the distributed .mjs
How to create a library exposing a single Vue component that can be consumed by the distributed .mjs

Time:02-02

I want to create a single Vue component that gets bundled into a single .mjs file. Another Vue project can fetch this .mjs file via HTTP and consume the component. Installing the pluggable component via npm is not possible, because the other application tries to fetch it based on a configuration during runtime.

Things to consider for the pluggable component

  • Might be using sub components from another UI framework / library
  • Might be using custom CSS
  • Might rely on other files e.g. images

Reproducing the library

I created a new Vuetify project via npm create vuetify

enter image description here

I deleted everything from the src folder except vite-env.d.ts , created a component Renderer.vue

<script setup lang="ts">
import { VContainer } from "vuetify/components"

defineProps<{ value: unknown }>()
</script>

<template>
  <v-container>
    <span >Value is: {{ JSON.stringify(value, null, 2) }}</span>
  </v-container>
</template>

<style>
.red-text { color: red; }
</style>

and an index.ts file

import Renderer from "./Renderer.vue";

export { Renderer };

I added the enter image description here


Consuming the component

I created a new Vue project via npm create vue

enter image description here

and for testing purposes I copied the generated .mjs file right into the src directory of the new project and changed the App.vue file to

<script setup lang="ts">
import { onMounted, type Ref, ref } from "vue";

const ComponentToConsume: Ref = ref(null);

onMounted(async () => {
  try {
    const { Renderer } = await import("./renderer.mjs"); // fetch the component during runtime

    ComponentToConsume.value = Renderer;
  } catch (e) {
    console.log(e);
  } finally {
    console.log("done...");
  }
});
</script>

<template>
  <div>Imported component below:</div>
  <div v-if="ComponentToConsume === null">"still loading..."</div>
  <component-to-consume v-else :value="123" />
</template>

Unfortunately I'm getting the following warnings and errors

[Vue warn]: Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with markRaw or using shallowRef instead of ref.

[Vue warn]: injection "Symbol(vuetify:defaults)" not found.

[Vue warn]: Unhandled error during execution of setup function

[Vue warn]: Unhandled error during execution of scheduler flush.

Uncaught (in promise) Error: [Vuetify] Could not find defaults instance

Does someone know what I'm missing or how to fix it?

CodePudding user response:

Vuetify doesn't provide isolated components and requires the plugin to be initialized, you need to do this in main app:

app.use(Vuetify)

Make sure vuetify isn't duplicated in project deps, so the lib and main app use the same copy.

The lib should use vuetify as dev dependency and specify it in Rollup external, in order to prevent the things that are global to the project from being bundled with the lib:

external: ["vue", "vuetify"]
  • Related