preface
Hello, I’m a sea monster. Those of you who are familiar with the micro front end should be familiar with the single-SPA framework, but I looked through the Chinese community and found too few articles about the single-SPA Demo practice.
There are some articles about it, but they are mainly based on the code, only about what, not why. It’s not a great experience for the reader. Let’s take a closer look at the React version of Single-SPA. Let the reader know what it is and what it is.
Introduction to the
In fact, the single-SPA website has many examples:
People should just focus on Actively maintained. Here I pick the first React Microfrontends to illustrate.
architecture
One point into the project, found that this thing has several projects:
The README. Md of root-config is so confusing that I believe it can persuade a lot of people.
However, I have them all down, and there are seven of them:
│ ├─ API # API Utils Call Function │ ├─ NavBar # NavBar │ ├─ People # People │ ├─ Plants │ plants │ ├ ─ ─ Shared - dependencies # import - map json file └ ─ ─ styleguide # Button component + global CSS stylesCopy the code
Their architecture looks like this:
The locations of these “small items” are shown below:
We can categorize the above “small projects” (other than root-config) as follows:
type | project |
---|---|
The main application | root-config |
Page components | people.plants |
Common components | navbar |
Public suite | api.styleguide |
JSON import-map | shared-dependencies |
Everything except root-config is microapplications, whether page components or common suites. Let’s go over these projects now.
root-config
Let’s start with root-Config. This project is the main application, or the “head” of the entire App, and its work is very clean:
- Introduce JS for common libraries and microapplications
- Define the page routing of the microapplication
- Load individual microapplications (pages/components/common functions/common styles)
Introduce JS for common libraries and microapplications
The introduction of common library, microapplication JS is implemented through SystemJS ‘import-map feature, such as this import-map.json:
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/[email protected]/umd/react.production.min.js"."react-dom": "https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.production.min.js"."single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js"."@react-mf/root-config": "https://react.microfrontends.app/root-config/55aa0b39bcca3e3d0844b2c0aa2f658a2aa1b94f/react-mf-root-config.js"."@react-mf/navbar": "https://react.microfrontends.app/navbar/c1a777c770ee187cebedd0724653c771495f2af9/react-mf-navbar.js"."@react-mf/styleguide": "https://react.microfrontends.app/styleguide/504c8516e30274fc0e3221a719d5355b14af9500/react-mf-styleguide.js"."@react-mf/people": "https://react.microfrontends.app/people/4d0af0a673764fa9b311f9f98163c88be9af9426/react-mf-people.js"."@react-mf/api": "https://react.microfrontends.app/api/849cde43d1bf1a072c1b71b4de504fc7120d4629/react-mf-api.js"."@react-mf/planets": "https://react.microfrontends.app/planets/010c38f36fc578c406f1aa44d16f6aad9062f2f2/react-mf-planets.js"."@react-mf/things": "https://react.microfrontends.app/things/7f209a1ed9ac9690835c57a3a8eb59c17114bb1d/react-mf-things.js"."rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/rxjs.min.js"."rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/rxjs-operators.min.js"."@react-mf/api/": "https://react.microfrontends.app/api/849cde43d1bf1a072c1b71b4de504fc7120d4629/"."@react-mf/people/": "https://react.microfrontends.app/people/4d0af0a673764fa9b311f9f98163c88be9af9426/"."@react-mf/navbar/": "https://react.microfrontends.app/navbar/c1a777c770ee187cebedd0724653c771495f2af9/"."@react-mf/planets/": "https://react.microfrontends.app/planets/010c38f36fc578c406f1aa44d16f6aad9062f2f2/"."@react-mf/styleguide/": "https://react.microfrontends.app/styleguide/504c8516e30274fc0e3221a719d5355b14af9500/"."@react-mf/root-config/": "https://react.microfrontends.app/root-config/55aa0b39bcca3e3d0844b2c0aa2f658a2aa1b94f/"}}Copy the code
This can be introduced in index.html with the
<script type="systemjs-importmap" src="https://storage.googleapis.com/react.microfrontends.app/importmap.json"></script>
Copy the code
Import React from ‘React’ in SystemJS. Here the react just from the above JSON in https://cdn.jsdelivr.net/npm/[email protected]/umd/react.production.min.js.
Define routes & load microapplications
Single-spa uses custom tags to control different routes to specify corresponding page components:
// index.html
<template id="single-spa-layout">
<single-spa-router>
<nav class="topnav">
<application name="@react-mf/navbar" loader="topNav" error="topNav"></application>
</nav>
<div class="main-content mt-16">
<route path="people">
<application name="@react-mf/people"></application>
</route>
<route path="planets">
<application name="@react-mf/planets"></application>
</route>
<redirect from="/earth" to="/planets"></redirect>
<route default>
<h1 class="flex flex-row justify-center p-16">
<p class="max-w-md">This example project shows independently built and deployed microfrontends that use React and single-spa. Each nav link above takes you to a different microfrontend.</p>
</h1>
</route>
</div>
</single-spa-router>
</template>
Copy the code
Note: template is not a template in Vue, but a custom tag, which is written in HTML.
To parse these labels, single-SPA created a library called Single-SPa-Layout:
// react-mf-root-config.js
import {
constructRoutes,
constructApplications,
constructLayoutEngine,
} from "single-spa-layout";
import { registerApplication, start } from "single-spa";
const routes = constructRoutes(document.querySelector("#single-spa-layout"), {
loaders: {
topNav: "<h1>Loading topnav</h1>",},errors: {
topNav: "<h1>Failed to load topnav</h1>",}});const applications = constructApplications({
routes,
loadApp: ({ name }) = > System.import(name),
});
// Delay starting the layout engine until the styleguide CSS is loaded
const layoutEngine = constructLayoutEngine({
routes,
applications,
active: false}); applications.forEach(registerApplication); System.import("@react-mf/styleguide").then(() = > {
// Activate the layout engine once the styleguide CSS is loaded
layoutEngine.activate();
start();
});
Copy the code
@react-MF/styleGuide, @react-MF /people, @react-MF /plants, @react-MF /navbar, These libraries are all from the.js in import-map.json defined above.
These @react-MF/XXX are both libraries and microapplications. This import-map library is a feature of SystemJS asynchronous JS, which is what Single-SPA has been emphasizing with JS Entry — loading microapplications through JS.
Webpack packaging
Although the entry JS is written very simply, we still need to package it as SystemJS to dynamically import it in index.html:
// index.html
<script type="systemjs-importmap">
{
"imports": {
"@react-mf/root-config": "//localhost:9000/react-mf-root-config.js"}}</script>.<script>
System.import('@react-mf/root-config');
System.import('@react-mf/styleguide');
</script>
Copy the code
Therefore, we need to think about how to configure Webpack as well. Alas, when it comes to Webpack, some brothers have begun to wear pain masks. Single-spa has also thought of this, so it has introduced the webpack-config-single-spa library to mindlessly configure Webpack:
const { merge } = require("webpack-merge");
const singleSpaDefaults = require("webpack-config-single-spa");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = (webpackConfigEnv, argv) = > {
const orgName = "react-mf";
const defaultConfig = singleSpaDefaults({
orgName,
projectName: "root-config",
webpackConfigEnv,
argv,
disableHtmlGeneration: true});return merge(
defaultConfig,
{
plugins: [
new HtmlWebpackPlugin({
inject: false.template: "src/index.ejs".templateParameters: {
isLocal: webpackConfigEnv && webpackConfigEnv.isLocal,
orgName,
},
}),
],
},
{
// modify the webpack config however you'd like to by adding to this object}); };Copy the code
Page components
There are two main components: people and plants. React implements both of these components.
// root.component.js Root component
import React from "react";
import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
import PeoplePage from "./people-page/people-page.component.js";
export default function Root(props) {
return (
<BrowserRouter>
<Route path="/people/:personId" component={PeoplePage} />
<Route path="/people" component={PeoplePage} exact />
</BrowserRouter>
);
}
Copy the code
Single-spa provides a single-spa-React library to help export life cycles:
/ / the react - mf - people. JSX entrance
import React from "react";
import ReactDOM from "react-dom";
import singleSpaReact from "single-spa-react";
const lifecycles = singleSpaReact({
React,
ReactDOM,
errorBoundary() {
return <div>Error</div>;
},
loadRootComponent: () = >
import(
/* webpackChunkName: "people-root-component" */ "./root.component.js"
).then((mod) = > mod.default),
});
export const bootstrap = lifecycles.bootstrap;
export const mount = lifecycles.mount;
export const unmount = lifecycles.unmount;
export function getFilmsComponent() {
return import(
/* webpackChunkName: "films-component" */ "./films/films.component.js"
);
}
Copy the code
Seeing this, I believe you will have a question: Single-SPA JS Entry should have high requirements for packaging, right? Alas, single-Spa thought of this too, so they also provided us with a webpack-config-single-spa-React library to help us better configure WebPack:
// webpack.config.js
const { merge } = require("webpack-merge");
const singleSpaDefaults = require("webpack-config-single-spa-react");
module.exports = (webpackConfigEnv = {}) = > {
const defaultConfig = singleSpaDefaults({
orgName: "react-mf".projectName: "people",
webpackConfigEnv,
});
const config = merge(defaultConfig, {
// customizations go here
});
return config;
};
Copy the code
Similarly, if you use vue, there is the equivalent of Webpack-config-single-spa-vue.
Common components
People and Plants are just components, but they are page-level components. So Navbar still uses single-spa-React to export the life cycle:
// react-mf-navbar.js
import React from "react";
import ReactDOM from "react-dom";
import singleSpaReact from "single-spa-react";
import Root from "./root.component";
const lifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: Root,
errorBoundary(err, info, props) {
return (
<div className="h-16 flex items-center justify-between px-6 bg-primary text-white">
Error
</div>); }});export const bootstrap = lifecycles.bootstrap;
export const mount = lifecycles.mount;
export const unmount = lifecycles.unmount;
Copy the code
Public suite
The common suite is more like Utils, with the API and StyleGuide microapps.
The entry file for the API is very simple:
// react-mf-api.js
export { fetchWithCache } from "./fetchWithCache.js";
Copy the code
Import functions directly on people and Plants using es6-like syntax via SystemJS import-map loading:
// utils/api.js
import { fetchWithCache } from "@react-mf/api";
export function getPeople(pageNum = 1) {
return fetchWithCache(`people/? page=${pageNum}`);
}
Copy the code
Styleguide’s most important actions are the introduction of global CSS and the export of a Button:
// react-mf-styleguide.js
import "./global.css? modules=false";
// You can also export React components from this file and import them into your microfrontends
export { default as Button } from "./button.component.js";
Copy the code
Seeing this, you might say: why not use the NPM import method? Seems to have nothing to do with the micro front end?
Yes, the common suite here really has nothing to do with a micro front end, except that Single-SPA treats this common library as a micro application. You can say it’s a micro app, it’s a micro app, but introduced in SystemJS style, it’s kind of like taking off your pants. And to dynamically import JS in index. HTML, you can only import third-party libraries in SystemJS mode.
import-map
Import-map is just a JSON file that holds some common resources:
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js"."react": "https://cdn.jsdelivr.net/npm/[email protected]/umd/react.production.min.js"."react-dom": "https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.production.min.js"."rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/rxjs.min.js"."rxjs/operators": "https://cdn.jsdelivr.net/npm/@esm-bundle/[email protected]/system/es2015/rxjs-operators.min.js"}}Copy the code
This project is not directly use the library at present, just quote this address storage.googleapis.com/react.micro… JSON file.
The import-map method of importing JS libraries was originally implemented in Chrome. The purpose of this method is to solve the problem of importing JS dynamically and writing it as ES6: Import React from ‘React’, and can dynamically load JS in index. HTML:
<script>
System.import('@react-mf/root-config');
System.import('@react-mf/styleguide');
</script>
Copy the code
P.S. SystemJS has been a way of packaging for a long time, so don’t worry: why not?
Note: Import-map here is not a proprietary feature of SystemJS and can be used on older browsers. However, SystemJS can handle some of the compatibility issues when platforms use import-Map.
conclusion
So, here’s a quick summary of single-SPA’s React-Microfrontends Example:
- Root-config is the “brain” of the entire application, which can also be understood as the main application, but the application is very simple, there is only one
index.html
And amain.js
Entry JS composition - All main and micro apps need to be packaged in SystemJS, and if you don’t know how to configure them, Single-SPA offers a number of Webpack widgets:
webpack-config-single-spa
.webpack-config-single-spa-react
, feel free to use - Microapplications include framework components, plain JS, and CSS. Component needs to
single-spa-xxx
To export components, ordinary JS is used to export ES6, CSS is usedimport 'xxx.css'
It is enough
I believe that after reading this article, you can also have enough knowledge to see other examples.
In this Example, we can see the drawbacks of the single-SPA framework:
- Too intrusive, not only to export the life cycle in the portal, but also to change the Webpack configuration
- JS Entry is a simple way to access micro applications. We prefer to access micro applications through an index.html URL
- Nothing, such as JS, CSS isolation, main micro application communication, etc. These need to be handled by other small libraries
In general, it is not recommended to use single-SPA for micro front end, but the MicroApp of QIANKKun or JINGdong is recommended.