Combining front-end frameworks/libraries using federated modules

Combining front-end frameworks/libraries using federated modules
July 9, 2020
I believe almost everyone has heard of the new killer feature called federated modules. In my opinion, with this feature we can either use already written open source modules and merge them into an app or it can be useful for a big project where different teams can create independent modules. Most importantly, all examples are presented in one ecosystem only (React library).

After reading this article about micro front-ends, I decided to write a micro front-ends application with federated modules features.
My first intention was to combine React and Svelte. But I ended up struggling with Svelte loader issues and found only one solution to provide aliases and main fields to webpack config. Unfortunately, this fix didn’t work in my case (github, stack overflow) . Therefore, I gave up and went for Vue.

How to combine them with each other

As a developer, you have basically 2 options:

  1. access module from window object
  2. import module and assign to DOM element

Access module from window object

Access through window object property can be done easily by accessing the library name which is defined in ModuleFederationPlugin. So something like this:
src: modules/vue-module/webpack.config.js

{% c-block language="js" %}
new  ModuleFederationPlugin({
   name: 'vue',
   library: { type:  "var", name:  "vue" },
   exposes: {App: './src/bootstrap'}
   ...etc
})
{% c-block-end %}

Then we can access module
src: modules/main/public/index.html

{% c-block language="js" %}
window.vue.get('App').then(module => {
const vueInstance = module().default;
vueInstance.$mount('#vue');
});
{% c-block-end %}

Basically, we have async import from which we can take our Vue instance through default export and mount it to some element in DOM.

Import module inside react component and assign it to DOM element

And here comes the fun part…

For mounting the Vue component you need:

  1. useRef:
    To append the child in which we will mount the Vue instance
  2. useEffect:
    To handle the React component life cycle and provide side effects
  3. document.createElement:
    For Vue instance mount <- this explicitly defined container must be provided because when Vue is mounted it will use the container for the template render.

It will look something like this: src: modules/main/src/VueContainer.jsx

{% c-block language="js" %}
import React, { useRef, useEffect } from 'react'
import app from 'vue/App';
const container = document.createElement('div');
function VueContainer() {
       const ref = useRef();
       useEffect(() => {
           const currentElement = ref.current;
           const vueInstance = app();
           if(currentElement) {
                  currentElement.appendChild(container);
                  vueInstance.$mount(container);
           }
           return () => {
                  vueInstance.$destroy();
                  container.remove();
           }
       }, [ref])
       return <div ref={ref}></div>
}
{% c-block-end %}

And it works, but …

Rendering issues

We will get the Vue instance, but the template will not receive props. Instead of a rendered component, we will get bunch of errors in which properties are defined in the template, but the instance does not have them. After several hours of searching, I found a great article about render functions in Vue. (I would like to state that I am a kind of a newbie in Vue. So, if you know better solutions, please let me know.) We need to override the default render function and explicitly provide the templates which we want to render. We will create a component with the Vue.component function which will have an inner state and which we will use. The other problem is re-rendering. Once the Vue instance is mounted, if the React component is unmounted, then the Vue component will be unmounted too and will not re-render again. So forced re-rendering should be provided. I haven’t found a better solution yet, except for a function which returns an instance of Vue on each React component mount, create a new instance and mount it to the container.

Something like this:
src: modules/vue-module/src/bootstrap.js

{% c-block language="js" %}
const app = () => new Vue({
   render: function(h) {
   const renderTemplate = this._c || h;
   return renderTemplate(App); // <- App.vue
   }
});
{% c-block-end %}

But this approach also has its flaws – with each re-render the Vue components lose their state

A few words at the end

This was a simple sandbox example of combining different frameworks and libraries. I am not sure if this could be used for production purposes, but it is a cool feature which is fun to play with and I want to explore it little bit more in the future.

Code example link

What is on my to-do list?

  • Try to combine with other frameworks / libraries
  • Find a solution to have a persistent state
Share:
Radmir is a Web and Mobile App Developer. His professional career started in 2018, but he has always been keen on learning new technologies. He has gained the most experience in Java/Typescript, React, MobX, and Express.js. He loves both music and computer games of any kind.

Other articles by same author

Article collaborators

About SABO Mobile IT

We focus on developing specialized software for our customers in the automotive, supplier, medical and high-tech industries in Germany and other European countries. We connect systems, data and users and generate added value for our customers with products that are intuitive to use.
Learn more about sabo

About SABO

We focus on developing specialized software for our customers in the automotive, supplier, medical and high-tech industries in Germany and other European countries. We connect systems, data and users and generate added value for our customers with products that are intuitive to use.
Learn more about sabo