Micro Frontend Solution 6 - Build & Deploy
Micro Frontend Build
There are some things to note when bundling micro frontend projects. Take webpack as an example:
AMD Modules
In previous articles, we mentioned that our loader is based on System.js.
So the final bundle of our micro frontend modules must conform to module specifications.
We use the AMD module specification to build our modules.
Specify Base Path
Since the module loader calls the module entry file after bundling,
To clearly manage each module and correctly load all our module resources,
we specify a publicPath for all module resources.
Below is a simple webpack configuration. I only list the necessary options. It’s not a complete webpack config. I’ll provide a complete micro frontend demo later for reference. These configurations are modified based on
create-react-app. Once you understand the intent of the config and what kind of bundle we end up with, no matter how the build tool changes or the tech stack changes, it can always be integrated into the micro frontend.
Here is the project.json content for reference when reading the configuration file:
// project.json
{
"name": "name", // module name
"path": "/project", // module url prefix
"prefix": "/module-prefix/", // module file path prefix
"main": "/module-prefix/main.js", // module render entry file
"store": "/module-prefix/store.js", // module external interface
"base": true // whether it's the base app
}
// Import project config file, one of the necessary files for the module loader
const projectConfig = require('./project.json')
let config = {
entry: {
main: paths.appIndexJs, // entry file, one of the necessary files for the module loader
store: paths.store // external api reducer file, one of the necessary files for the module loader
},
output: {
path: paths.appBuild,
filename: '[name].js?n=[chunkhash:8]',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
publicPath: projectConfig.prefix, // specify the configured publicPath in output
libraryTarget: 'amd', // output AMD module for system.js loading
library: projectConfig.name, // module name
},
},
module: {
rules: [
{
oneOf: [
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
// loader: 'happypack/loader?id=url',
loaders: [{
loader: require.resolve('url-loader'),
options: {
limit: 5000,
name: 'static/media/[name].[hash:8].[ext]',
publicPath: projectConfig.prefix, // add publicPath to static file loaders
},
}]
},
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
loader: 'happypack/loader?id=babel',
options: {
name: 'static/js/[name].[hash:8].[ext]',
publicPath: projectConfig.prefix, // add publicPath to static file loaders
},
},
{
loader: require.resolve('file-loader'),
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
publicPath: projectConfig.prefix, // add publicPath to static file loaders
},
},
],
},
],
},
}
Deployment
No matter how front-end SPA deployment is automated or how tools change, it’s always about placing the bundled static files in the correct location on the server. Micro frontend deployment is a process of application aggregation. How do we finally integrate each module into a complete project?
Micro Frontend Application Directory
It’s usually placed in a static directory configured by nginx, or another web container’s static directory.
Looking at this directory structure, you should understand why we need to configure publicPath.
├── index.html // The browser loads this index.html first, which imports a bootstrap.js file
├── bootstrap.js // This bootstrap.js is the module loader bundled code.
│ // The module loader first loads `project.config.js` to get all module configs.
│ // Then it loads each module's main.js, registers the app, and injects store.js
│
├── project.config.js // This file stores all module configs for the project, auto-generated
│ // Mentioned earlier as project config automation — the only dynamic file in the project.
│ // When modules are updated or new modules are added, they're automatically mounted.
│ // It iterates through each module's project.json, extracts content, and merges them
│
├── projectA // Module A directory
│ ├── asset-manifest.json
│ ├── favicon.ico
│ ├── main.js // Render entry file
│ ├── manifest.json
│ ├── project.json // Module config file
│ ├── static
│ │ ├── js
│ │ │ ├── 0.86ae3ec3.chunk.js
│ │ └── media
│ │ └── logo.db0697c1.png
│ └── store.js // Exported store.js file
└── projectB // Module B (important file locations are same as Module A)
├── asset-manifest.json
├── main.js
├── manifest.json
├── project.json
├── static
│ ├── js
│ │ ├── 0.86ae3ec3.chunk.js
│ └── media
│ └── logo.db0697c1.png
└── store.js
Config Automation
Each of our modules has the config file described above. When our project has multiple modules, we need to aggregate all module config files. I also wrote a script for this:
Usage:
npm install micro-auto-config -g
# Start the script with pm2 in the project root directory to enable config automation
pm2 start micro-auto-config --name your-project-name-auto-config
After this, project.config.js will be auto-generated, and will be regenerated when modules change.
To be continued …
Related Articles
Micro Frontend Solution 1 - Thinking
Micro Frontend Solution 2 - Single-SPA
Micro Frontend Solution 3 - Module Loader
Micro Frontend Solution 4 - Message Bus
Micro Frontend Solution 5 - Route Distribution
Micro Frontend Solution 6 - Build & Deploy
Micro Frontend Solution 7 - Static Data
Micro Frontend Solution 8 - Secondary Build
Demo
Micro Frontend Base App Source
Micro Frontend Submodule Source