Micro frontend with Vue + Vite

If you already know what micro frontend is, jump to the implementation section.

Consider you are in a context like this: you work on a frontend project that has many business units, with their own rules and logic. One team is assigned to take care of one of these business units. The problem is, all of these business units are contained in the same frontend project (monolith), putting in the same codebase different business rules and logic. This project grows, and as a consequence of this, you, and of course the other teams, are facing some troubles, like:

  1. Deployments taking so long
  2. Huge amount of time to get tests executed
  3. Coupled codebases, without a clear separation of contexts
  4. Teams depending each other to do simple works

What to do in a case like this?

This is an already known problem in backend development. Microservices was the approach used to solve this problem on the backend side. So, what’s the approach used on the front end side? Micro frontends!

So, in a nutshell, basically what we’re talking about here is: various parts of a large application separated into small parts, each of these parts contained in a repository, and being taken care of by a team. The images below are more descriptive:

Source: Martin Fowler
A microfrontend developed in Vue, another one developed in Angular, and the third one developed in React. All of them togheter, forms just one application for the user
Source: cygnismedia

Advantages and drawbacks

The advantages here are clear, this approach solves (of course, aligned with team policies and organization) all the problems contained in the first section. But considering that nothing is a silver bullet for the problems we are talking about, micro frontend can lead to problems like:

  1. Project bundle size, due to package duplication between micro frontends. This can be avoided depending on the approach you use to do micro frontends. In our implementation case, using vite-plugin-federation, we can configure the packages we are going to use from the host application, instead install it twice.
  2. A lot of independence of teams can lead to some anarchy. This is not a really bad point if the company deals well with this. Otherwise, this can be avoided by defining some team policies, like a moderator team evaluating pull requests and others.

Besides the previous points, there are some cases micro frontend is not the ideal fit, like its implementation in small projects, for example.

So, the answer to the question “should I change to micro frontend?” is basically the answer for everything in software engineering: it depends.

If you want to go deep in micro frontend concepts, check this arcticle.

Implementation

This explanation is based on this repo

Conceptually this application simulates a courier, which by its nature contains some transportation-related modules. Here I wanted to just create just two of them, shipment management and shipment tracking.

Technically speaking, it is composed majorly in four parts (that are folders on the previous repo), that are:

  1. pigeon (red) — This part of the application takes use of the micro frontend host concept, which is the “container” of all the remote applications, host application “children”.
  2. Tracker application (blue) — one of the remote (“child”) applications, responsible for show tracking data related to a specific shipment
  3. Shipments application (green) — one of the remote (“child”) applications, responsible for showing all the user shipments
  4. NPM custom elements package — project that consists of an NPM package, that aims to reduce code reimplementation around stateless components, like buttons, input texts, tables, etc. All the components on this package can be reused on the three previous projects

How is everything connected?

The first three projects cited previously are connected via vite-plugin-federation, a library that allows developers to use the module federation concept with the Vite bundle tool. Summing up, module federation is an approach that consists in build applications in a way to allow them to be re-used on other applications.

On the remote side, what you should do to serve this project to the host side, is install vite-plugin-federation, and use it in vite.config.ts like this:

import { defineConfig } from "vite";
import federation from "@originjs/vite-plugin-federation";

export default defineConfig({
...
preview: {
port: 5005, //port you want to serve this remote
},
plugins: [
...
federation({
name: "pigeon-tracker", //name of remote you want to use on host side
filename: "pigeonTracker.js", //filename after the build
exposes: {
"./App": "./src/App.vue", //target component you want to serve as remote side. In our case is the entire application
},
shared: ["vue"], //we don't want to build our remote with a library the host side already have. So here we sinalize "hey, use this host side package"
}),
],
});

After configuring the remote side, we want to configure the host side. This can be done this way:

import { defineConfig } from "vite";
import federation from "@originjs/vite-plugin-federation";

export default defineConfig({
...
plugins: [
...
federation({
name: "pigeon-app", //app name
remotes: {
pigeon_tracker: "http://localhost:5005/assets/pigeonTracker.js", //remote path containing the port configured on remote side, the build path, and the filename also configured on the remote side
}
}),
],
});

Now that both sides are configured, if you are using Typescript on the host project, you must tell it that you are going to use a custom module. This can be done by creating a d.ts file, in my case, remotes.d.ts

declare module "pigeon_tracker/App"; //the name of the remote configured on the host side / the name of the module defined on the remote application

Now, to use your remote side in some place, you must have in mind that it must be done in an asynchronous way. Considering that Vue Router offers us a way to load routes asynchronously, we are going to use routes to load our remotes. It was done this way:

import { createRouter, createWebHistory } from "vue-router";
import HomeView from "../views/HomeView.vue";

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "home",
component: HomeView,
},
{
path: "/tracker",
name: "tracker",
component: () => import("pigeon_tracker/App"), //the name defined on host's vite.config.ts, referring to remote name/remote name
},
],
});

export default router;

This way, when the user access http://localhost:hostPort/tracker, he/she is going to get access to an independent application, through our host. This is completely transparent to the user and amazingly fluid to developers.

If you have any doubts about how to implement this, check this amazing documentation

Then you can ask “and the components used commonly in the three projects, I will have to develop them many times, right?”. There are many known ways to reuse developed components through many applications. Module federation, by the way, is a good way to do it. But I prefer to use the most known way to reuse stateless components, that is NPM packages.

Using element-plus and ag-grid to not develop components from scratch, I reused four components from these packages, just to prove the concept. The components reused were: Button, Input, Table, and Timeline. Now, I just need to install the package in the project I want to use the component and use it normally. The project will have a consistent UI since the styling will be the same, and reimplementations for the same component will be not necessary.

If you want to check how the package was implemented, take a look at the project in the repo.

Micro frontend is an amazing architecture to be used in complex projects and teams. But use it wisely, and have in mind that all architectures and tools have drawbacks. Consider them, do your evaluation, and your life will be happy.

I hope you enjoyed it! If you have any questions, just tell me!

See u

Implementation: https://github.com/vitorlofonseca/microfrontend-vue-and-vite

References: https://martinfowler.com/articles/micro-frontends.html

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store