前言
最近在做微后端开发。 当我遇到子应用程序资源问题而无法共享时,我了解到webpack5可以动态运行另一个JavaScript应用程序的代码,同时共享一个依赖项。 由于框架使用Umi进行开发,官方没有提供配置示例。 如果您在使用过程中遇到一些陷阱,请记录下来。
模块联盟
Module Federation 中文直译为“模块联邦”。 在webpack官方文档中,实际上并没有给出它的真正含义,但是给出了使用这个功能的动机。
多个独立的构建可以形成一个应用程序。这些独立的构建不会相互依赖,因此可以单独开发和部署它们。
这通常被称为微前端,但并不仅限于此
Webpack 配置模块联合
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
// 其他webpack配置...
plugins: [
new ModuleFederationPlugin({
name: 'app',
library: { type: 'umd', name: 'app' },
filename: 'app.js',
remotes: {
app_1: "app_1_remote",
app_2: "app_2_remote"
},
exposes: {
'./Button': 'src/components/Button',
'./Component2': 'src/components/Component2',
},
shared: ["react", "react-dom"]
})
]
}
通过上面的配置,我们对mf有了一个初步的了解,也就是说,如果我们想要使用mf,我们需要配置几个重要的属性:
字段名称 类型 含义
姓名
细绳
所需的值是输出模块名称。 远程引用时,路径为name/{name}/name/{expose}
图书馆
细绳
声明全局变量的方法webpack 公共库,名称为umd的名称
文件名
细绳
构建输出的文件名
遥控器
目的
映射远程引用的应用程序名称及其别名,使用时使用键值作为名称
暴露
目的
远程引用时可以公开的资源路径及其别名
共享
目的
可以与其他应用程序共享的第三方依赖项,无需在代码中重复加载相同的依赖项
umi框架使用
首先我们通过脚手架创建两个项目,分别是mf1,端口3000,导出一些组件Button等,项目mf2,端口3001,引入mf1的Button组件。我们看一下mf1的配置
项目 mf1 配置
我们项目mf1的主要功能是提供MF能力并导出Button组件以供其他应用程序使用。
const { ModuleFederationPlugin } = require("webpack").container;
export default defineConfig({
// 其他umi配置...
publicPath:'//localhost:3000/',
webpack5: {},
chainWebpack(memo) {
memo
.plugin('mf')
.use(ModuleFederationPlugin, [{
name: "mf1",
library: { type: 'umd', name: 'mf1' },
filename: 'remoteEntry.js',
exposes: {
"./Button": './src/components/button/index',
},
shared: { react: { eager: true }, "react-dom": { eager: true } }
}])
},
});
这里的 umi 配置中,需要启用 webpack5 并添加 publicPath。 特别注意配置publicPath。 如果不配置,其他应用程序将无法加载导入的模块。
按钮组件
import React, { FC } from 'react';
const Button = props => {
return (<div><button>我是mf1的button</button></div>);
}
export default Button;
项目 mf2 配置
我们项目mf2的主要功能是提供MF能力webpack 公共库,为当前应用引入远程mf1应用组件。
中频配置
const { ModuleFederationPlugin } = require("webpack").container;
export default defineConfig({
// 其他umi配置...
webpack5: {},
dynamicImport:{},
chainWebpack(memo) {
memo
.plugin('mf')
.use(ModuleFederationPlugin, [{
name: "mf2",
remotes: {
mf1: "mf1@//localhost:3000/remoteEntry.js"
}
shared: { react: { eager: true }, "react-dom": { eager: true } }
}])
},
});
这里的umi配置中,需要启用webpack5并添加dynamicImport。 特别注意配置dynamicImport,避免报错。
使用 mf1 项目组件
由于mf提供了异步加载,所以在使用组件时我们不得不依赖React的React.lazy和React.Suspense来加载异步组件。
// 导入组件
const Mf1Button = React.lazy(() => import("mf1/Button"));
export default function IndexPage() {
return (
<div>
<React.Suspense fallback='loading'>
<Mf1Button />
</React.Suspense>
</div>
);
}
配置说明 Umi 框架使用问题
通过上面的简单配置,我们运行了MF,但是出现了以下问题:
解决公共路径
通过查看MF文档,publiPath可以是auto。 它将手动为您确定一个 publicPath。 但如果我们直接配置的话,就会出错。 umi 框架会验证是否以 / 开头,以符合 publicPath 规则。我们尝试修改 webpack 方法来解决问题,配置为
chainWebpack(memo) {
memo.output.publicPath('auto');
// 其它webpack配置
},
使用webpack。问题已经解决。 至此,部署问题已经解决。
解决组件中无法使用Hooks的问题
MF一定要记得加共享。 这个配置可以为我们共享依赖。为了解决这个问题,官方要求我们设置异步导出的入口,参考链接
但 umi 不支持异步启动。 我们可以通过编写插件来支持功能和插件内容。
import { IApi } from 'umi';
import { resolve } from 'path';
import { readFileSync } from 'fs';
export default (api: IApi) => {
api.onGenerateFiles(() => {
const buffer= readFileSync(resolve('./src/.umi/umi.ts'))
const c = String(buffer)
// console.log()
api.writeTmpFile({
path: 'index.ts',
content: c,
});
api.writeTmpFile({
path: 'umi.ts',
content: 'import("./index")',
});
});
};
主要是先获取 umi 生成的入口文件。 我们自己创建一个文件,将内容写入其中,然后更改入口文件的内容。 我已经制作了一个 npm 包。 您可以直接使用它。 该插件名为 umi-plugin-mf-bootstrap,我们只安装yarn install umi-plugin-mf-bootstrap。重启项目,umi 会手动加载插件
结论
至此我们就完成了 umiModule Federation 的使用。 我们在制作微后端的时候也使用了qiankun和umi的qiankun-plugin插件。 我们写的 umi-plugin-mf-bootstrap 会不兼容,不能同时使用。 我们也在积极探索,如果有进展就会发布。
参考链接
MF-演示
umi-插件-mf-bootstrap
模块联盟
umi 插件
如果您觉得文章不错,不妨点个关注
1.点赞,让更多人看到此内容
发表评论