了解模块联邦二
上篇博文提到了了解模块联邦,这篇文章我简单介绍一下如何实现模块联邦。
首先准备好对应的工具以及项目:
- share:共享模块的项目,用于提供可共享的模块。可将其作为一个远程模块,其他项目可以通过模块联邦来引入共享模块。
- host-vite:主机模块的项目
- host-rsbuild:主机模块的项目
- host-webpack:主机模块的项目
share
首先,不管share使用那种构建工具(rslib, vite, webpack)都好,因为share只是提供可共享的模块,不参与业务逻辑。
例如:share 我需要共享hooks,utils, enums, components 等模块, 但是对应的hooks和components都是依赖vue 和 element-plus,所以在share中需要引入这两个库。
接下来,我将使用rslib作为基层去构建share项目;使用rslib的原因很简单:Rslib 支持宿主应用和 Rslib 模块联邦项目同时开发。 可参考文档。当然也可以换其他的构建工具。
配置如下:
// config.ts
import {
AutoImport,
Components,
DefineConfig,
ElementPlusResolver,
PluginUnpluginVue,
RsdoctorRspackPlugin,
type RslibConfig,
PluginSass,
} from "./plugin";
import format from "./format";
// 仅在需要时启用 RSDoctor(通过环境变量控制)
const shouldEnableRsdoctor = process.env.RSDOCTOR === "true";
const config: RslibConfig = {
server: {
port: Number(process.env.APP_PORT),
cors: true,
},
output: {
target: "web",
minify: {
js: true,
css: true,
},
sourceMap: false,
filenameHash: false,
},
lib: format, //=========== 具体的核心代码在这里
plugins: [PluginUnpluginVue(), PluginSass()],
tools: {
rspack: {
plugins: [
AutoImport.default({
resolvers: [ElementPlusResolver()],
imports: ["vue"],
dts: true,
}),
Components.default({
resolvers: [ElementPlusResolver()],
dts: true,
}),
// 显式配置 RSDoctor,避免自动检测警告
...(shouldEnableRsdoctor
? [
new RsdoctorRspackPlugin({
// 配置选项可以根据需要调整
}),
]
: []),
],
},
},
};
export default DefineConfig(config);需要安装的依赖如下:
//plugin.ts
/**
* @fileoverview 导出 rslib 的配置和插件, 用于构建 shared 库
*/
export {
defineConfig as DefineConfig,
type RslibConfig,
type LibConfig,
} from "@rslib/core";
/**
* @fileoverview 导出 rsbuild 的插件, 用于构建 shared 库, 包含 vue 的插件
*/
export { pluginUnpluginVue as PluginUnpluginVue } from "rsbuild-plugin-unplugin-vue";
/**
* @fileoverview 导出 rsbuild 的插件, 用于构建 shared 库, 包含 module-federation 的插件
*/
export { pluginModuleFederation as PluginModuleFederation } from "@module-federation/rsbuild-plugin";
/**
* @fileoverview 导出 rsbuild 的插件, 用于构建 shared 库, 包含 auto-import 的插件
*/
export * as AutoImport from "unplugin-auto-import/rspack";
/**
* @fileoverview 导出 rsbuild 的插件, 用于构建 shared 库, 包含 vue-components 的插件
*/
export * as Components from "unplugin-vue-components/rspack";
/**
* @fileoverview 导出 rsbuild 的插件, 用于构建 shared 库, 包含 element-plus 的插件
*/
export { ElementPlusResolver } from "unplugin-vue-components/resolvers";
/**
* @fileoverview 导出 rsdoctor 的插件, 用于构建分析和诊断
*/
export { RsdoctorRspackPlugin } from "@rsdoctor/rspack-plugin";
/**
* @fileoverview 导出 rsbuild 的插件, 用于构建 shared 库, 包含 sass 的插件
*/
export { pluginSass as PluginSass } from "@rsbuild/plugin-sass";核心代码如下:
import { PluginModuleFederation, type LibConfig } from "./plugin";
const DIST_PATH = "./dist";
const ASSET_PREFIX = process.env.APP_REMOTE_URL;
const sharedLib = {
vue: {
singleton: true,
eager: true,
requiredVersion: "^3.2.0",
},
"element-plus": {
singleton: true,
eager: true,
requiredVersion: "^2.12.0",
},
};
const lib: LibConfig[] = [
// ================== 核心代码
// ASSET_PREFIX 换成你的域名, 开发环境需要和你项目的端口一致,生产环境需要和你部署的域名一致
// 执行的文件会放在 dist/mf目录下
{
format: "mf",
output: {
// 输出的目录
distPath: `${DIST_PATH}/mf`,
assetPrefix: `${ASSET_PREFIX}/mf`,
},
dev: { assetPrefix: `${ASSET_PREFIX}/mf` }, // 开发环境使用
plugins: [
PluginModuleFederation({
filename: "remoteEntry.js", // 输出的文件名
name: "shared_remote", // 模块联邦的名称
exposes: {
// 暴露的模块, /src/index.ts 是我存放的 utils. hooks. enums
".": "./src/index.ts",
// 暴露的组件模块, 例如button, table 等
"./components": "./src/components/index.ts",
},
shared: sharedLib, // 共享的库
shareStrategy: "version-first", // 优先使用版本匹配的共享模块
}),
],
},
// 支持浏览器环境, 可以通过cdn的方式引入,组件的话不一定能正常使用,可正常使用 utils. hooks. enums 等
{
format: "iife",
umdName: "sharedRemote",
output: {
distPath: `${DIST_PATH}/iife`,
assetPrefix: `${ASSET_PREFIX}/iife`,
},
dev: { assetPrefix: `${ASSET_PREFIX}/iife` },
tools: {
rspack: {
output: {
library: {
name: "sharedRemote",
type: "window",
},
},
},
},
},
];
export default lib;