微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。各个前端应用还可以独立运行、独立开发、独立部署。
不同于市面上的Qiankun、MicroApp等框架,本期的框架为基于Webpack5的一个插件,Module Federation 构建的一套微前端体系。
主要有两个概念 HostWebApplication(主工程)、 RemoteWebApplicaton (远程库工程、依赖工程,可多个,库工程之间亦可作为主副依赖)
下面主要以三个独立工程做例子讲解 HostEntry工程,Common库工程,Module 模块工程。
项目核心结构
本文项目均基于 TS+React Hooks 讲述。
项目通用代码结构
---- ${Project name}
----dist
----node_modules
----public
----src
----.gitgnore
----babel.config.js
----index.html
----package-lock.json
----package.json
----README.md
----tsconfig.json
----webpack.config.js
**与常规项目的主要差异在webpack.config.js 及 /src下的入口 **
下面我们看下代码,首先是webpack.config部分,为了便利,我们省去其他代码
库工程配置文件解析
//通用库的 webpack.config.js
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = () => {
return {
devServer: {
open: true,
port: 3003, //项目端口,用于区分不同前端应用
proxy: {
"/": "http://${your_api_url}}:80", //网络请求base url
},
},
...
plugins [
...
new ModuleFederationPlugin({
name: "***_***_***", // 与package.json内name保持一致
filename: "remote.js", //远程应用(模块)构建出来的文件名称,可提供该文件给其他应用(模块)使用
exposes: {
//对外提供的组件,表示远程应用可以使用哪些内容
"./****EntryRCTSX" : "./src/***/***EntryRC.tsx",
"./****EntryRCJSX" : "./src/***/***EntryRC.jsx",
"./utils": "./src/***/***utils.js",
}
shared: {
...dependencies,
react: {
eager: true,
},
},
}),
].filter(Boolean),
};
};
以上是作为通用库工程的基本配置,何谓通用库,就是只允许被其他微前端应用调用,而不调用任何库工程的项目, 即前文提到过的 *Common库工程 *。
Module工程配置简述
接下来,我们谈一下 *Module 模块工程 * 的配置
//库工程的 webpack.config.js
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = () => {
return {
devServer: {
open: true,
port: 3005, //项目端口,用于区分不同前端应用
proxy: {
"/": "http://${your_api_url}}:80", //网络请求base url
},
},
...
plugins [
...
new ModuleFederationPlugin({
name: "***_***_***", // 与package.json内name保持一致
filename: "remote.js", //远程应用(模块)构建出来的文件名称,可提供该文件给其他应用(模块)使用
exposes: {
//对外提供的组件,表示远程应用可以使用哪些内容
"./****EntryRCTSX" : "./src/*ModuleName*/index.tsx",
"./****EntryRCJSX" : "./src/*ModuleName*/index.jsx",
},
remotes: {
*库工程name*:"*库工程name*@http://${lib_app_url}/remote.js",
},
shared: {
react: {
requiredVersion: false,
singleton: true,
},
},
}),
].filter(Boolean),
};
};
细心的读者注意应该注意到了,以上配置项多了remotes: {...} 配置项,即我们所依赖的远程微前端工程。
与单纯发布库工程不同,作为需要调用依赖common库的Module工程,需要对常规的入口文件index.tsx做细微调整,增加 ** bootstrap.js** 文件,并将其作为应用入口,其代码如下:
//bootstrap.js
import App from './App';
import React from 'react';
import { createRoot } from 'react-dom/client';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/es/locale/zh_CN';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<ConfigProvider locale={zhCN}>
<App />
</ConfigProvider>);
index.tsx更新如下:
import("./bootstrap");
// eslint-disable-next-line import/no-anonymous-default-export
export default {};
以上操作是为了保证项目所依赖的远程应用都加载完毕,再启动本应用。
主入口工程配置简述
最后,我们来看如何集成上述项目到我们对用户展示的应用上面
主工程通用拥有上述配置文件,内容大体与 Module 工程相同,下面我们来看样例:
//Host主入口工程的 webpack.config.js
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = () => {
return {
devServer: {
open: true,
port: 3099, //项目端口,用于区分不同前端应用
proxy: {
"/": "http://${your_api_url}}:80", //网络请求base url
},
},
...
plugins [
new HtmlWebpackPlugin({
template: "index.html", //发布应用的入口模板
}),
...
new ModuleFederationPlugin({
name: "***_***_entry", // 与package.json内name保持一致
remotes: {
*库工程name*:"*库工程name*@http://${lib_app_url}/remote.js",
*模块工程name*:"*模块工程name*@http://${md_app_url}/remote.js",
},
shared: {
react: {
requiredVersion: false,
singleton: true,
},
},
}),
].filter(Boolean),
};
};
细心的朋友应该注意到了,本项目没有 filename、exposes 选项,因为我们不需要让其他应用调用。
同样,由于引用的远程项目,我们需要通过bootstrap.js启动应用。
代码如下:
//bootstrap.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import { HashRouter } from 'react-router-dom';
import BootIndex from './BootIndex';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<HashRouter>
<BootIndex/>
</HashRouter>
);
index.tsx更新如下:
import("./bootstrap");
// eslint-disable-next-line import/no-anonymous-default-export
export default {};
以上操作是为了保证项目所依赖的远程应用都加载完毕,再启动本应用。
小结
前文所述,为微前端的项目配置,分为Host、Module、CommonLib三个项目,建议大家多动手尝试,下一期,会为大家简单讲述本文最后部分提到的 项目引用、路由配置等问题。