Module Federation In Vuejs Applications
Hey guys, so in this article, I am going to talk about module federation in VueJS applications. Before we start, let me share something special with you. I have an amazing blog up and running, where you can find even more insightful content. If you’re curious and eager to learn, make sure to click here and explore the wonders that await you.
I’m sure not many of y’all know what module federation is but if you are among the few that know, a big congrats to you.
Okay, Let’s jump right in.
What is Module Federation
Module federation is a JavaScript architecture invented by Zack Jackson. This architecture allows the sharing of code and dependencies between two different application codebases.
The code is loaded dynamically, and if a dependency is missing, the dependency will be downloaded by the host application, which allows for less code duplication in the application.
WHAT ARE MICRO-FRONTENDS
The concept of micro-frontends has been gaining traction in recent times. The push for microservices has also brought about the same implementation to the modern web in the form of micro-frontends. As the monolith app scales, it becomes difficult to maintain, especially across several teams working on the same app.
We can look at micro-frontends as feature-based, where there are different teams and each team handles a particular feature component while another team handles something else. In the end, all teams merge the different components they have built to form one application.
Developers made use of frameworks like single-spa and OpenComponents to achieve this, but with the new release of webpack 5 and the module federation, we can easily achieve the same goal, but way easier.
ADVANTAGES OF MICRO-FRONTENDS
Adopting a micro-frontend approach to building your web applications is probably the best strategy. This is especially true if you are building a large-scale web application with many moving parts or applications that are branched out into sub-applications where you want some consistency in the overall look.
Let me highlight a few reasons you might want to switch to the micro-frontend approach:
- Adopting a micro-frontend approach will allow us to create an end-to-end feature architecture. This approach will allow us to develop and deploy features locally, without the need for large-scale deployment infrastructures
- With a smaller and more optimized bundle size, micro-frontends provide an overall better developer and user experience as a result of shared components and dependencies that can be lazy-loaded whenever we want
- One of the biggest advantages for me is the ability for teams of developers working on that particular product to be able to pick a technology stack of their choice without fear of incompatibility with the other team’s code
How do we split our apps?
These are some ways developers split large apps:
- By page — In our frontend applications, sometimes having different pages run at the same time in the browser can cause a crash in older devices, so the safest way is to split by page. If you have good routing, you can run separate, specific micro-apps for every page, which is also good for the developers on your team, because they will always work on that one page assigned to them
2. By functionality — If you have one page with multiple things features performing different operations, you can split those big features into smaller apps and make it an independent application running that specific feature
3. By section — You can also split your applications by section, with different apps sharing the same section or components.
I’m sure most of y’all just want to scream slow down bro at the top of your voices.
Okay, I’ll just give a summary of what I just said. You know how we create components in Vue, now imagine we had two different apps for one company. For instance, we had a shop app and a merchant app. These apps would probably use similar components such as headers, buttons, inputs, and so on.
In this case, we do not want to keep on creating components every time we create a new Vue application, that’s where module federation comes in. Technically we are sharing components between apps without having to create these components. I’d get to the code part in a minute.
An added advantage of using module federation in your enterprise application is that each component can be managed by a team and this team wouldn’t have to bother about any other thing in your application but the component they are assigned. Now imagine how great your select component would be if you have a team solely assigned to work on this component.
Okay, enough with the chit chat let’s continue.
Proof of concept
We have explained some concepts about micro-frontends and module federation. Now it’s time for a proof of concept.
Here, we will demonstrate how we can use the module federation to create micro-frontends in Vue. To test this out, we will be spinning up two different apps, so we can create a micro-frontend in one of them and share it with the other.
First, we create a folder to host the two Vue applications:
mkdir vue-mf
It is in the vue-mf folder we will run our Vue application. We won’t be using the Vue CLI here. Instead, we will be using the new release of webpack, which is webpack 5, to set up the Vue application.
We will name the two applications we want to share components as Company and Shop respectively. We create a folder for each of them in the vue-mf folder and then grab a webpack starter file of Vue created by Jherr from GitHub into each folder:
git clone https://github.com/jherr/wp5-starter-vue-3.git
Let’s take a look at the file structure now that we have set up the app:
+ — vue-mf/
| + — Company/
| + — Shops/
When we open up one of the app folders, this is the structure:
+ — vue-mf/
| + — Company/
| + — src/
| + — App.vue
| + — bootloader.js
| + — index.css
| + — index.html
| + — index.js
| + — package.json
| + — webpack.config.js
| + — Shops/
Set up webpack config
So we have two apps, Company and Shop, which are the same for now. When we survey the file structure, we take a look at the package.json. We have our webpack loader, CSS loader, and all the basic loaders and webpack stuff we need:
If we take a look at the webpack.config.js file, we can see that our public path is set to port 8080. We can also see webpack checking for our file extensions and using the appropriate loaders.
The important thing to take note of here is our Vue loader plugin used in parsing our files and the Module federation plugin from webpack 5 we have imported and used, which will allow us to perform sharing functionality. We will get back to the configuration of the ModuleFederationPlugin later in this tutorial.
N.B., make sure to set the public path and dev server port in the other application (Shop) to port 8081, so we can be able to run both apps simultaneously.
Create components to be shared
In our application, the App.vue file will serve as the homepage, so let’s add some markup:
The header component is one part of an application we would like to share between applications. Let’s say one of the teams of developers decides to build the header, so we create a header component that we can share in the two applications.
In the src folder in our Company app, we will create a header component. To do this, we create a Header.vue file, and in it, we create the header component:
After creating the header, navigate to App.vue and import the header component:
We can now start our development server by navigating to each folder and running:
yarn && yarn start
Right now our app looks like this in the Company app.
Exposing the header component through the Module Federation plugin
We now have our header in the Company app, we would like to use it in the Shop app. So we head over to the webpack configuration in the Company app:
In the webpack Module Federation configuration, we set the name to the app name, which is Company, and remoteEntry.js to be our filename. When we navigate to the remoteEntry.js file name, we see the code related to the components and dependencies we want to share. We also exposed the header component with its location.
Now, if we restart our server and navigate to http://localhost:8080/remoteEntry.js, we will see this:
Now grab the remote entry URL and switch to the webpack configuration file in our Shop app:
Here, we give the plugin the name of Shop and set the remote to remoteEntry URL. Then in our App.vue file in the Shop app, we import and use the header component from our Company app:
If we restart our server, we can see that the shop page now has the header component, meaning we have successfully shared the component between the two apps. Yay!
N.B., if the team working on the header decides to push a new update for the header component, the Shop app team will immediately see the update once the Shop app is refreshed.
Sharing App State Between Federation Modules
Let’s say you are using a state manager in your Vue application like Vuex. You might be asking yourself how you might have the same state shared between the two components, and also have it updated. So, let’s install Vuex for both apps:
yarn install vuex@next
Once we have installed Vuex, navigate to the bootloader.js file in our Company app, where we initialize our Vue app.
Here, we import our store and create a state:
If, for instance, this is an eCommerce store where we want to display the number of cart items we have in our cart, we create a cartItems state and display it in our company header. Then go to our header component, access the state, and display it:
We have successfully set up our state, but the problem with this is if we start the server for both apps and check the Company app, we can see the header display with the state:
But if we navigate to the Shop app, we can no longer see the shared header component anymore, much less the state we added. Instead, we get an error message that says we can’t read the state of undefined because, in our Shop app, we haven’t set up any store.
To rectify this problem, we copy all the code we have in the bootloader of the Company app and paste it into the bootloader.js file of the Shop app. This time, we changed the cartCount state to 12. If we restart the server, we now have our header in the Shop app, with cart items of 12.
Let’s say we want to mimic the addition of more shop items to the cart, so in the Shop app, we add a button that increments the cartCount state:
If we restart the Shop application, we can see that the items in the header are now updated. Yay!
Resources
Conclusion
We have come to the end of this tutorial.
Here, we discussed how to dramatically simplify our app architecture by using webpack 5’s Module Federation to consume and share micro-frontend components with example code.
Whether you should or should not adopt micro-frontends depends on the kind of project you are building, because this approach will not be the best for small applications or businesses. A micro-frontend architectural approach is your best bet when working on a large project with distributed teams.
Thanks for getting to the end of this doc.
Much Love from Bolathedev ❤️