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.
As a developer, you have basically 2 options:
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.
And here comes the fun part…
For mounting the Vue component you need:
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 …
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
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?