Single-Spa 实践-- 拆分子应用不同目录
Single-Spa 实践(下)-- 拆分子应用不同目录
在上篇中讲到了,在同一个目录下创建主应用以及子应用,但是这样的话其实还是感觉像一个大应用,并且别的模块在维护的时候还需要把整个项目 clone 下来,感觉还是欠缺了点什么。
在这篇中大致上还是基于上看的结构,接着把上面的结构完全拆开来,拆分到单独的 repo,可以分为如下几个结构:
1. 根(主)应用: root-config
2. 子应用1: app1
3. 子应用2: app2
接着一步步来
Step 1
把 app1 和 app2 从 micro-front 文件下拆分出来,与 micro-front 同级,基本需要包含如下的文件
|—— micro-front
|——— index.ejs // 页面
|——— package.json
|——— webpack.config.config
|——— single-spa.config.js // 主应用的的 single-spa 配置
|—— app1
|——— src
|——— App.js // 子应用
|——— react-mf-app1.js // 子应用的 single-spa 配置
|——— set-public-path.js // 暴露子应用
|——— package.json
|——— webpack.config.config
|—— app2
|——— src
|——— App.js
|——— react-mf-app2.js
|——— set-public-path.js
|——— package.json
|——— webpack.config.config
Step 2
修改 mirco-front 的 webpack 配置
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = () => {
const result = {
entry: path.resolve(__dirname, "./single-spa.config"),
output: {
filename: "single-spa.config.js",
libraryTarget: "system",
path: path.resolve(__dirname, "dist"),
},
devtool: "sourcemap",
module: {
rules: [
{ parser: { system: false } },
{
test: /\.js$/,
exclude: /node_modules/,
use: [{ loader: "babel-loader" }],
},
],
},
devServer: {
historyApiFallback: true,
disableHostCheck: true,
headers: {
"Access-Control-Allow-Origin": "*",
},
https: true,
},
plugins: [
new HtmlWebpackPlugin({
template: "./index.ejs",
}),
new CleanWebpackPlugin(),
],
externals: ["single-spa", /^@react-mf\/.+$/],
};
return result;
};
修改 index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>React Microfrontends</title>
<meta name="importmap-type" content="systemjs-importmap" />
<script type="systemjs-importmap">
{
"imports": {
"@react-mf/root-config": "//localhost:9000/single-spa.config.js"
}
}
</script>
<script src="https://cdn.jsdelivr.net/npm/import-map-overrides@2.1.0/dist/import-map-overrides.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.7.1/dist/system.min.js"></script>
<template id="single-spa-layout">
<single-spa-router>
<div class="main-content mt-16">
<route path="app1">
<application name="@react-mf/app1"></application>
</route>
<route path="app2">
<application name="@react-mf/app2"></application>
</route>
<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>
</head>
<body>
<a href="/app1">app1</a>
<br>
<a href="/app2">app2</a>
<script>
System.import('@react-mf/root-config');
</script>
</body>
</html>
Single-sap.config.js
import {
constructRoutes,
constructLayoutEngine,
constructApplications,
} from "single-spa-layout";
import { registerApplication, start } from "single-spa";
console.log(registerApplication);
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),
});
const layoutEngine = constructLayoutEngine({
routes,
applications,
active: false,
});
applications.forEach(registerApplication);
layoutEngine.activate();
start();
到这里,主应用的改造就算完成了,你可以单独运行一下主应用,切换到子应用路由,页面控制台会报未找到子应用的错误,是因为子应用没有改造完且没有运行,所以是找不到
Step 3
改造完主应用接下来来改造一下子应用
首先改造 webpack.config.js,因为子应用是我们直接通过 react 的 CRA 脚手架创建的,懒得把内置的配置暴露出来,所以自己就增加一个 webpack 配置,同样的在 package.json 中增加一条命令
webpack.config.js
const webpackMerge = require("webpack-merge");
const singleSpaDefaults = require("webpack-config-single-spa-react");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = (webpackConfigEnv = {}) => {
// TODO: 这部分具体干嘛的还不怎么清楚,感觉像是对外暴露这个包名的
const defaultConfig = singleSpaDefaults({
orgName: "react-mf",
projectName: "app1",
webpackConfigEnv,
});
const config = webpackMerge.smart(defaultConfig, {
devServer: {
historyApiFallback: true,
https: true,
},
plugins: [
new HtmlWebpackPlugin(),
],
resolve: {
extensions: [".js", ".jsx", ".ts", ".tsx"],
},
externals: [/^rxjs\/?.*$/],
});
return config;
};
Package.json 增加一条 script
"startDev": "webpack-dev-server --port 9001"
React-mf-app1.js
const webpackMerge = require("webpack-merge");
const singleSpaDefaults = require("webpack-config-single-spa-react");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = (webpackConfigEnv = {}) => {
const defaultConfig = singleSpaDefaults({
orgName: "react-mf",
projectName: "app1",
webpackConfigEnv,
});
const config = webpackMerge.smart(defaultConfig, {
// customizations go here
devServer: {
historyApiFallback: true,
https: true,
},
plugins: [
new HtmlWebpackPlugin(),
],
resolve: {
extensions: [".js", ".jsx", ".ts", ".tsx"],
},
externals: [/^rxjs\/?.*$/],
});
return config;
};
Set-public-path.js
import { setPublicPath } from "systemjs-webpack-interop";
setPublicPath("@react-mf/app1");
自此,子应用的也算是改造完了,app2 参考着 app1 改造就行。
Step 4
运行,配置 importmap ,分别启动主应用和子应用
⚠️ 这里使用的是 https ,所以注意自己的 url
运行起来后,我们可以看到我们主应用的页面,切换的子应用的路由控制台会报错,这是因为我们还是实际配置 importmap
在这之前先推荐安装一下 single-spa 的一个 chrome 扩展 single-spa-inspector
,这个扩展可以在我们运行 single-spa 的微服务应用的时候帮助我进行一些配置,以及做一个子应用的切换
安装了扩展之后我们的 Chrome 控制台就会多这么一个 tab ,在这个 tab 下就能看到我们的子应用有哪些,初始 import override 那是没有配置的,这里根据我们自己的子应用做一个配置就行,上面就是我的配置,到这里算是完成了,我们切换路由就可以看到对应的子应用了。
本文地址:https://blog.csdn.net/F_Felix/article/details/110585819