Application developed using React, vue and svelte programming languages and Webpack 5's new Module Federation. In this project, I use tailwindcss to style components.
Easily, put these code into the head tag in html file of each micro app:
<link href="https://unpkg.com/[email protected]/dist/tailwind.min.css" rel="stylesheet" />
To create micro frontend applications using Webpack Module Federation, you can follow those steps bellow:
###1. I begin to build Footer application by VueJS:
Install following packages:
In /src, create Footer.vue component
<template>
<div class="m-2 p-5 bg-green-200"> Footer - Vue </div>
</template>
<template>
<div class="text-3xl mx-auto max-w-6xl">
<Footer/>
</div>
</template>
<script>
import Footer from './Footer.vue'
export default {
components:{
Footer
}
}
</script>
import { createApp } from "vue";
// window.headers.get('./Header').then((data) => {
// const Header = data().default;
// new Header({
// target:document.getElementById("header")
// })
// })
import "./index.scss";
import App from "./App.vue";
createApp(App).mount("#app");
import("./bootstrap.js");
import { createApp } from "vue";
import Footer from './Footer.vue';
export default (selector) => {
createApp(Footer).mount(selector);
}
const HtmlWebPackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const { VueLoaderPlugin } = require("vue-loader");
module.exports = (_, argv) => ({
output: {
publicPath:
argv.mode === "development"
? "http://localhost:3002/"
: "https://luca-webpack-mfe-footer.surge.sh/",
},
resolve: {
extensions: [".tsx", ".ts", ".vue", ".jsx", ".js", ".json"],
},
devServer: {
port: 3002,
historyApiFallback: true,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods":
"GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers":
"X-Requested-With, content-type, Authorization",
},
},
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.tsx?$/,
use: [
"babel-loader",
{
loader: "ts-loader",
options: {
transpileOnly: true,
appendTsSuffixTo: ["\\.vue$"],
happyPackMode: true,
},
},
],
},
{
test: /\.(css|s[ac]ss)$/i,
use: ["style-loader", "css-loader", "postcss-loader"],
},
],
},
plugins: [
new VueLoaderPlugin(),
new ModuleFederationPlugin({
name: "footers",
filename: "remoteEntry.js",
exposes: {
"./Footer": "./src/Footer.vue",
"./mountFooter": "./src/mountFooter",
},
shared: require("./package.json").dependencies,
}),
new HtmlWebPackPlugin({
template: "./src/index.html",
}),
],
});
I named this micro frontend app: footers. You can see in the line:
name: "footers",
We need to set a name for the chunk file:
filename: "remoteEntry.js"
To use component from Footer micro app, we need to put it in the "box":
exposes: {
"./Footer": "./src/Footer.vue",
"./mountFooter": "./src/mountFooter",
},
npm start
###2. Create micro app "container", this app is the container contains our other micro frontends.
Install following packages:
Similar to Vue app, we mount id #app in bootstrap.js:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("app"));
import("./bootstrap");
Important
<meta
http-equiv="Content-Security-Policy"
content="upgrade-insecure-requests"
/>
This way helps our app can run in the production environment. Read more: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/upgrade-insecure-requests
const HtmlWebPackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const deps = require("./package.json").dependencies;
module.exports = (_, argv) => ({
output: {
publicPath:
argv.mode === "development"
? "http://localhost:3001/"
: "https://luca-webpack-mfe-container.surge.sh/",
},
resolve: {
extensions: [".tsx", ".ts", ".jsx", ".js", ".json"],
},
devServer: {
port: 3001,
historyApiFallback: true,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods":
"GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers":
"X-Requested-With, content-type, Authorization",
},
},
module: {
rules: [
{
test: /\.m?js/,
type: "javascript/auto",
resolve: {
fullySpecified: false,
},
},
{
test: /\.(css|s[ac]ss)$/i,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.(ts|tsx|js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: "container",
filename: "remoteEntry.js",
// remotes: {
// footer:
// argv.mode === "development"
// ? "footers@http://localhost:3002/remoteEntry.js"
// : "footers@https://luca-webpack-mfe-footer.surge.sh/remoteEntry.js",
// header:
// argv.mode === "development"
// ? "headers@http://localhost:8080/remoteEntry.js"
// : "headers@https://luca-webpack-mfe-header.surge.sh/remoteEntry.js",
// products:
// argv.mode === "development"
// ? "Products@http://localhost:3003/remoteEntry.js"
// : "Products@https://luca-webpack-mfe-products.surge.sh/remoteEntry.js",
// payment:
// argv.mode === "development"
// ? "Payment@http://localhost:3004/remoteEntry.js"
// : "Payment@https://luca-webpack-mfe-payment.surge.sh/remoteEntry.js",
// },
remotes: {
footer: "footers@https://luca-webpack-mfe-footer.surge.sh/remoteEntry.js",
// header: "headers@https://luca-webpack-mfe-header.surge.sh/remoteEntry.js",
products:
"Products@https://luca-webpack-mfe-products.surge.sh/remoteEntry.js",
payment: "Payment@https://luca-webpack-mfe-payment.surge.sh/remoteEntry.js",
},
exposes: {},
shared: {
...deps,
react: {
singleton: true,
requiredVersion: deps.react,
},
"react-dom": {
singleton: true,
requiredVersion: deps["react-dom"],
},
},
}),
new HtmlWebPackPlugin({
template: "./src/index.html",
}),
],
});
name: "container",
We need to set a name for the chunk file:
filename: "remoteEntry.js"
To use micro app in container, set remotes line:
remotes: {
footer: "footers@https://luca-webpack-mfe-footer.surge.sh/remoteEntry.js",
// header: "headers@https://luca-webpack-mfe-header.surge.sh/remoteEntry.js",
products:
"Products@https://luca-webpack-mfe-products.surge.sh/remoteEntry.js",
payment: "Payment@https://luca-webpack-mfe-payment.surge.sh/remoteEntry.js",
},
import mountFooter from "footer/mountFooter";
const RelatedProducts = React.lazy(() => import("products/App"));
const ReactButton = React.lazy(() => import("payment/ReactButton"));
const AmountItem = React.lazy(() => import("payment/AmountItem"));
After importing components, we can use them in container micro-app
If 2 micro-apps are 1 framework / library, you can use them directly.
const RelatedProducts = React.lazy(() => import("products/App"));
Otherwise, if 1 app built in ReactJS, another app built in VueJS, you must mount the component to the DOM to use. Because Webpack Module Federation need times to load data, we import mounting component to use micro app indirectly.
mountFooter("#footer");
In index.html file:
<body>
<!-- <div id="header"></div> -->
<div id="app"></div>
<div id="footer"></div>
</body>
Install the dependencies and devDependencies and start the server.
cd headers
npm install
npm start
cd container
npm install
npm start
cd products
npm install
npm start
cd footer
npm install
npm start
cd payment
npm install
npm start
At the first time run the project, you can run the following commands. Because we use file start.sh to start project, so we must grant permission to this file:
sudo chmod +x start.sh
Then enter your PC password
Run
./start.sh
To run the applications, use the following command:
npm run start
Prog. Lang. | PORT |
---|---|
REACTJS | :3002 :3003 :3004 |
VUEJS | :3001 |
SVELTEJS | :8080 |
Deployment: I use Surge.sh to deploy the applications.
Page | Link |
---|---|
container | https://luca-webpack-mfe-container.surge.sh/ |
products | https://luca-webpack-mfe-products.surge.sh/ |
header | https://luca-webpack-mfe-header.surge.sh/ |
footer | https://luca-webpack-mfe-footer.surge.sh/ |
payment | https://luca-webpack-mfe-payment.surge.sh/ |
MIT