Chapter 3: Single-SPA micro-front-end framework
zh-hans.single-spa.js.org/
Single-spa: https://single-spa.js.org/ is a framework for implementing a micro-front-end architecture.
There are three types of microfront-end applications in the single-SPA framework:
- Single-spa-application/Parcel: a micro application in a micro front-end architecture, using vue, React, Angular, etc.
- Single-spa root config: Create a micro front-end container application.
- Utility Modules: Public module applications, non-rendering components used for micro applications that share javascript logic across applications.
3-1 Creating a container application
Install single-SPA scaffolding tools: NPM install [email protected] -g
Create a micro front-end container application: create-single-SPA
-
Application folder Fill in Container
-
Select single-SPA root Config for application
-
Enter study for organization name
The organization name can be understood as the team name. The micro front-end architecture allows multiple teams to develop applications together, and the organization name identifies which team is developing the application.
Application names are named @ organization name/application name, such as @study/todos
4. Start the application: CD./singletest && NPM start
5. Access the application: localhost:9000
3-2 Container default code parsing
src/xx-root-config.js
// Import two methods from the framework, called below
import { registerApplication, start } from "single-spa";
Name: a string of characters. The micro front-end application name is "@organization name/application name". App: ActiveWhen: Activates the application */ when the route matches
registerApplication({
name: "@single-spa/welcome".app: () = >
System.import(
"https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js"
),
activeWhen: ["/"]});Copy the code
// Examples of microapplications introduced in the current organization
// registerApplication({
// name: "@xl/navbar",
// app: () => System.import("@xl/navbar"),
// activeWhen: ["/"]
// });
// The start method must be called in the configuration file of single SPA
// Before calling start, the application is loaded, but not initialized, mounted, or unloaded.
start({
// Whether single-SPA routing can be triggered with history.pushState() and history.replacestate () changes
// true disallow false allow
urlRerouteOnly: true});Copy the code
index.ejs
<body>
<main></main>
<! -- Import the micro front-end container application -->
<script>
System.import('@xl/root-config');
</script>
<! -- import-map-overrides Can override the import mapping used in the current project with the single-SPA Inspector debugging tool. You can manually override the JavaScript module loading address in your project for debugging.
<import-map-overrides-full show-when-local-storage="devtools" dev-libs></import-map-overrides-full>
</body>
Copy the code
<! Support Angular apps -->
<! -- < script SRC = "https://cdn.jsdelivr.net/npm/[email protected]/dist/zone.min.js" > < / script > -- >
<! -- Override JavaScript module download address set by import-map -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/import-map-overrides.js"></script>
<! -- Check if it is local -->
<% if (isLocal) { %>
<! -- Module loader -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/system.js"></script>
<! -- SystemJS plugin for parsing AMD modules -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/extras/amd.js"></script>
<% } else { %>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/system.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/extras/amd.min.js"></script>The < %} % >Copy the code
<script type="systemjs-importmap">
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js"}}</script>
<! -- Single-SPA preloading -->
<link rel="preload" href="https://cdn.jsdelivr.net/npm/[email protected]/lib/system/single-spa.min.js" as="script">
<! -- Add your organization's prod import map URL to this script's src -->
<! -- <script type="systemjs-importmap" src="/importmap.json"></script> -->
<! Public modules in the micro front-end project can be placed here -->
<% if (isLocal) { %>
<script type="systemjs-importmap">
{
"imports": {
"@xl/root-config": "//localhost:9000/xl-root-config.js"}}</script>The < %} % >Copy the code
3-3 Creating a React microapplication
Create the React micro application
Create the app: create-single-spa, note the organization and project name, which will be used to register the micro-app later
- Apply directory type todos
- I’m gonna go to React
Modify the application port && to start the application
{
"scripts": {
"start": "webpack serve --port 9002",}}Copy the code
Start the application: NPM start
3-3-2 Registration applications
Register the React project entry file with the base application (container application)
\ container \ SRC \ study – root – config. Js:
// React -- todos
registerApplication({
name: "@study/todos".app: () = > System.import("@study/todos"),
activeWhen: ["/todos"]});Copy the code
Specify the reference address of the micro front-end application module:
(Can directly access the corresponding application server, there is a prompt URL loading address)
<% if (isLocal) { %>
<script type="systemjs-importmap">
{
"imports": {
"@study/root-config": "//localhost:9000/study-root-config.js"."@study/todos": "//localhost:9002/study-todos.js"}}</script>The < %} % >Copy the code
By default, react and react-dom are not packaged by Webpack. Single-spa considers them public libraries and should not be packaged separately.
<script type="systemjs-importmap">
{
"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"}}</script>
Copy the code
Modify the default application code to display application content on an independent page
container\src\study-root-config.js
// registerApplication({
// name: "@single-spa/welcome",
// app: () =>
// System.import(
// "https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js"
/ /),
// activeWhen: ["/"],
// });
// Change the default application registration mode to display application content on a separate page
registerApplication(
"@single-spa/welcome".() = > System.import(
"https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js"
),
location= >location.pathname === '/'
);
Copy the code
3-3-3 specifies the application render location
micro\container\src\index.ejs
<body>
<main></main>
<h2>
<! -- Specify application display location -->
<div id="myreact"></div>
</h2>
<script>
System.import('@study/root-config');
</script>
<import-map-overrides-full show-when-local-storage="devtools" dev-libs></import-map-overrides-full>
</body>
Copy the code
micro\todos\src\study-todos.js
const lifecycles = singleSpaReact({
React,
ReactDOM,
// Render the root component
rootComponent: Root,
// Error boundary function
errorBoundary(err, info, props) {
// Customize the root error boundary for your microfrontend here.
return null;
},
// Specify the render location of the root component
domElementGetter:() = >document.getElementById('myreact')});Copy the code
3-3-4 React applies code parsing
micro\todos\src\study-todos.js
import React from "react";
import ReactDOM from "react-dom";
Single-spa-react is used to create a micro-front-end application implemented using the React framework
import singleSpaReact from "single-spa-react";
// The root component used to render the page is the app.js file of the traditional React App
import Root from "./root.component";
const lifecycles = singleSpaReact({
React,
ReactDOM,
// Render the root component
rootComponent: Root,
// Error boundary function
errorBoundary(err, info, props) {
// Customize the root error boundary for your microfrontend here.
// return null;
return () = > <div>This content will be rendered if an error occurs</div>
},
// Specify the render location of the root component
domElementGetter:() = >document.getElementById('myreact')});// Expose the necessary lifecycle functions
export const { bootstrap, mount, unmount } = lifecycles;
Copy the code
3-3-5 React micro – front-end routing configuration
Prepare two routing components
micro\todos\src\home.js && micro\todos\src\about.js
import React, { Component } from 'react'
export default class home extends Component {
render() {
return (
<div>
<h2>What is happy Planet</h2>
</div>)}} ========= I am a beautiful and colorful dividing line between two components =============import React from 'react'
function about() {
return (
<div>
<h2>Happy Planet is learning about the micro front end</h2>
</div>)}export default about
Copy the code
micro\todos\src\root.component.js
import React from "react";
// Import routing-related components
import {BrowserRouter, Switch, Route, Redirect, Link} from "react-router-dom"
// Import components
import Home from './home'
import About from './about'
export default function Root(props) {
// return
{props.name} is mounted! &&pull big front
;
return (
// Design the base routing path using routing components
<BrowserRouter basename="/todos">
<div>{props.name}</div>{/* Set click link, jump route */}<div>
<Link to="/home">Home</Link> |
<Link to="/about">About</Link>
</div>{/* Route display */}<Switch>
<Route path="/home">
<Home />
</Route>
<Route path="/about">
<About></About>
</Route>
<Route path="/">{/* Redirection */}<Redirect to="/home"></Redirect>
</Route>
</Switch>
</BrowserRouter>)}Copy the code
The routing file has been introduced in the public module, \ Micro \container\ SRC \index.ejs
<script type="systemjs-importmap">
{
"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"."react-router-dom": "https://cdn.jsdelivr.net/npm/[email protected]/umd/react-router-dom.min.js"}}</script>
Copy the code
Modify the webpack configuration file to exclude routing module packaging, micro\todos\webpack.config.js
return merge(defaultConfig, {
// modify the webpack config however you'd like to by adding to this object
externals: ["react-router-dom"]});Copy the code
3-4 Create vUe-based microapplications
3-4-1 Creating an application
Create an app: create-single-SPA
- Fill in the project folder realWorld
- Select Vue for the framework
- Generate the Vue 2 project
Vue && Vue-Router needs to be packaged by a public module, so it needs to be configured not to be packaged within the application
micro\realworld\vue.config.js
module.exports = {
chainWebpack:config= >{
// Configure not to package Vue and vue-router
config.externals(["vue"."vue-router"])}}Copy the code
Modify the project startup command: micro\ realWorld \package.json
"scripts": {
"serve": "vue-cli-service serve"."start": "vue-cli-service serve --port 9003"."build": "vue-cli-service build"."lint": "vue-cli-service lint"."serve:standalone": "vue-cli-service serve --mode standalone"
},
Copy the code
Register application: micro\container\ SRC \study-root-config.js
// Vue -- todos
registerApplication({
name: "@study/realworld".app: () = > System.import("@study/realworld"),
activeWhen: ["/realworld"]});Copy the code
micro\container\src\index.ejs
Load vue && Vue-router
<script type="systemjs-importmap">
{
"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"."react-router-dom": "https://cdn.jsdelivr.net/npm/[email protected]/umd/react-router-dom.min.js"."vue": "https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"."vue-router": "https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js"}}</script>
Copy the code
Import applications. The application address can be directly accessed and obtained from a browser prompt.
<% if (isLocal) { %>
<script type="systemjs-importmap">
{
"imports": {
"@study/root-config": "//localhost:9000/study-root-config.js"."@study/todos": "//localhost:9002/study-todos.js"."@study/realworld": "//localhost:9003/js/app.js"}}</script>The < %} % >Copy the code
3-4-2 applies the routing configuration
\micro\realworld\src\main.js
import Vue from 'vue';
import singleSpaVue from 'single-spa-vue';
import App from './App.vue';
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// Routing component
const Foo = { template: "<div>Foooooo</div>" }
const Bar = { template: "<div>Barrrrr</div>" }
// Routing rules
const routes = [
{ path: '/foo'.component: Foo },
{ path: '/bar'.component: Bar },
]
// Route instance
const router = new VueRouter({ routes, mode: "history".base: "/realworld" })
Vue.config.productionTip = false;
const vueLifecycles = singleSpaVue({
Vue,
appOptions: {
// Register the route
router,
render(h) {
return h(App, {
props: {
// Pass parameters to the component}}); ,}}});export const bootstrap = vueLifecycles.bootstrap;
export const mount = vueLifecycles.mount;
export const unmount = vueLifecycles.unmount;
Copy the code
micro\realworld\src\App.vue
<template>
<div id="app">
<div>
<router-link to="/foo">Goto Foo</router-link> |
<router-link to="/bar">Goto Bar</router-link>
</div>
<div>
<router-view></router-view>
</div>
</div>
</template>
Copy the code
3-5 Create Utility Modules
3-5-1 Utility independent application created
Used to place JavaScript logic shared across applications, it is also a standalone application that needs to be built and launched separately.
-
Create an app: create-single-SPA
- Folder Fill in tools
- Application of choice
in-browser utility module (styleguide, api cache, etc)
-
Modify port, start application, \micro\tools\package.json
"scripts": { "start": "webpack serve --port 9005",}Copy the code
Export public method: micro\tools\ SRC \study-tools.js
export function happyStar(who){
console.log(`${who} hahahhahahhah`)
return 'The Source of Happiness of Happy Star'
}
Copy the code
Declare the application module access address in the template file: micro\container\ SRC \index.ejs
<% if (isLocal) { %>
<script type="systemjs-importmap">
{
"imports": {
"@study/root-config": "//localhost:9000/study-root-config.js"."@study/todos": "//localhost:9002/study-todos.js"."@study/realworld": "//localhost:9003/js/app.js"."@study/tools": "//localhost:9005/study-tools.js"}}</script>The < %} % >Copy the code
3-5-2 uses this method in React applications
MicroFrontends\micro\todos\src\about.js
import React, { useEffect, useState } from "react";
// Custom hook functions
function useToolsModule() {
const [toolsModule, setToolsModule] = useState();
useEffect(() = > {
// Import, asynchronous promise returns
System.import("@study/tools").then(setToolsModule); } []);return toolsModule;
}
function about() {
var back = "";
// Call the hook function
const toolsModule = useToolsModule();
if (toolsModule) {
// Call a method of shared logic
back = toolsModule.happyStar("React todo");
}
return (
<div>
<h2>Happy Planet is learning the microfront --{back}</h2>
</div>
);
}
export default about;
Copy the code
3-5-3 The method is used in Vue applications
micro\realworld\src\main.js
// Routing component
// const Foo = {template: "
"};
import Foo from './components/Foo'
const Bar = { template: "
or happy planet lol
" };
Copy the code
micro\realworld\src\components\Foo.vue
</button> </div> </template> <script> export {{MSG}}</h2> </button @click="getHappy" default { data() { return { msg: "", }; }, methods: { async getHappy() { let toolsModule = await window.System.import("@study/tools"); this.msg = toolsModule.happyStar("Vue"); ,}}}; </script> <style> </style>Copy the code