2

We are developing a micro frontend approach using Vue.js + Vuetify.js to build our webclient. The whole distributed System consists of many micro services that perform some task and produces some data. The main idea is that each service also provide an encapsulated compiled Vue component (fragment) that displays the data of the microservice and can be fetched and rendered by the client on demand on runtime. So each service has the following structure:

- ui
- fragments
- service specific source code

ui just contains the Vue + Vuetify app created with vue/cli. The fragment is than a normal Vue component like following:

<template>
  <v-menu
          ...
        >
          <template v-slot:activator="{ on }">
            <v-text-field
              ...
              v-on="on"
            ></v-text-field>
          </template>
          <v-date-picker v-model="date" no-title @input="menu1 = false"></v-date-picker>
        </v-menu>
</template>

<script>
export default {
  name: 'HelloWorld'
  ...
};
</script>

This component is compiled to a bundle helloworld.umd.min.js and provided from the service as static file:

vue-cli-service build --mode production --target lib --inline-vue --formats umd-min --watch --name helloworld ./src/components/HelloWorld.vue

On the client side we than binding the fragment on specific page on runtime by creating a <script src="http://localhost:2000/helloworld.umd.min.js"> tag with URL pointing to the service fragment. When the browser fetches the bundle a HelloWorld component is availiable and rendered using Vue dynamic components like this:

// component is the HellowWorld bundled component
<component :is="component" v-bind="bindProps"></component>

This approach is not perfect but works in generall. In most cases the so bundled components are fetched, rendered and displayed as a pice of the client without any issues. However in the above example component we are using a Vuetify DatePicker from the Vuetify examples. Rendering this fragment on the client side leades to a complete browser tab crash so the tab still not responsible. I already found out that this is coused by the scoped slot, so removing the folowing slot definition will fix the browser crash:

<template v-slot:activator="{ on }">...

Also other complex Vuetify components are working not properly by using the described approach. For example Vuetify Carousel or Tabs components (tabs) are renderend without tabs content like the following screenshot shows: enter image description here

Main questions

What can cause the described rendering issues by compiling nested Vuetify components? What would be the right approach to compile Vue.js + Vuetify.js components to a bundle to avoid this issues?

Any disscusions would be helpful!

Community
  • 1
  • 1
Serg
  • 825
  • 7
  • 13

1 Answers1

1

Edit: Although I am suggesting several solutions here, I have not tested any of it and it should be taken with a grain of salt. I have started using microfrontends recently, so I have just a rough idea of how they work. Don't hesitate to fix my answer if I say something wrong

I was asking myself the same question. I suspect that this weird behavior is being caused by treeshaking. I think that if you use Vuetify across all your components, you could use a script tag with vuetify outside the bundle script you will import.

<script src="http://vuetify.cdn" />

<!-- All below should be microfrontends, suposed to be written in Vue with Vuetify -->
<script src="helloworld.umd.min.js" />
<script src="foo.umd.min.js" />
<script src="bar.umd.min.js" />

If all your microfrontends are based on Vuetify you could change your webpack config to not bundle vuetify and use it in the macrofrontend globally through a CDN. This would have a drawback, however, if any of your frontends are made with Angular, the user would download Vuetify and not use it, and you should find a way to download the libs one by one. This should be manageable using the context of the import method.

The lib single-spa should allow you to do something like this:

registerComponent({
  renderOn: "/hello-world",
  import: () => {
    if (!window.vuetify) {
      window.vuetify = true;
      const script = document.createElement("script")
      script.src = "http://vuetify.cdn"
      document.appendChild(script);
    }
    
    return import("helloworld.umd.min.js")
  }
})

I know that it seems hacky, but that should do the trick, preventing your user to download what they don't need at the time and allowing you more control over the flow of downloads, having vuetify cached locally on their browser

Allan Lima
  • 155
  • 1
  • 10
  • Thanks for sharing your thoughts. The frontend is only Vue and Vuetify based. What I didn't mention in the question is that I imported the Vuetify components directly into the fragment to make it work in the first place. I also had to initialize Vuetify in the fragment. Initializing Vuetify globally via CDN is an interesting idea. Not sure if it would imply other drawbacks in our build process. The whole approach is quite naive. You might want to look at special libs like [Luigi](https://luigi-project.io/). – Serg Feb 10 '21 at 12:14
  • ... Currently my team is focused on other things. We are waiting for Vue 3 maturity. Then we want to tackle the problem again. Feel free to post your findings here. – Serg Feb 10 '21 at 12:14