ModuleFederation模块联邦落地尝试

654 阅读3分钟

我正在参加「掘金·启航计划」

截屏2022-10-04 11.16.51.png

前言


webpack5的module federation(模块联邦) 已趋于稳定, 面对这一“新”特性, 落地项目也该提上日程了.

背景


这次的背景项目是个包含PC网站、运营端、用户端、H5端、用户H5端、其它页面端的多端项目, 基于react16+ant4+redux开发; 下一步规划是微前端; 面对越来越多的跨项目重复组件, 模块联邦可谓是完美解决方案.

原理


webpack官网上面说的很明白了, 各位大佬也做了很多解析, 一句话解释(瞎掰): 模块联邦就是远程异步组件.

动手


关键配置位于webpack的plugins内, 通过引入container内的ModuleFederationPlugin进行配置; 关键配置为name、library、filename、exposes、remotes、shared:

  • name: 容器名称
  • filename: 容器地址名称
  • exposes: 暴露的组件名称
  • remotes: 远程容器地址(例: ${name}http://demo.com/${filename})
  • shared: 本地与远程容器共享的基础组件(库)

公共配置

const webpack = require("webpack");
const { ModuleFederationPlugin } = webpack.container;

{
...
output: {
    publicPath: `/`,
},
}

提供容器配置

  • 必须配置name、filename、exposes以供远程使用;
  • 可选配置library、shared
plugins: [
    new ModuleFederationPlugin({
        // 可自行定义
        name: 'base',
        // 实测type定义无影响, name需与外层一致
        library: { type: "umd", name: "base" },
        // 可匹配拆分chunk
        filename: "js/remoteEntry.js",
        exposes: {// 全部导出的组件
            // 名称根据版本加前缀: ./
            // 地址为相对路径, 相对此文件
            './RankList': './src/rank-list/index.js'
        },
        shared: {// 使用容器内与此处保持一致
            "react": {
                singleton: true,
                eager: true,
                requiredVersion: '*',
            },
            "react-dom": {
                singleton: true,
                eager: true,
                requiredVersion: '*',
            },
        }
    }),
]

使用容器配置

  • 必须配置remotes用以指定动态远程容器地址;
  • 可选配置shared: 公共依赖项,
plugins: [
    new ModuleFederationPlugin({
        remotes: {
            // key可项目内自行定义, 
            // name为提供容器内name字段
            // https://localhost:8090/ 需修改为提供容器的项目构建后部署地址
            // js/remoteEntry.js 需修改为提供容器的项目的filename, 即output内publicPath+ModuleFederationPlugin内的filename
            key: `name@https://localhost:8090/js/remoteEntry.js`,
        },
        shared: {// 与提供容器内一致
            "react": {
                singleton: true,
                eager: true,
                requiredVersion: '*',
            },
            "react-dom": {
                singleton: true,
                eager: true,
                requiredVersion: '*',
            },
        }
    }),
]

使用

引入远程组件需使用React.lazy(>=react16.8)方式,

const RankList = React.lazy(() => import('key/RankList'));

const RankListWrap = props => {
    return <>
        <React.Suspense fallback={<Spin />}>
            <RankList  {...props} />
        </React.Suspense>
    </>
}

此时大功告成.

注意事项


除去webpack上已知的错误, 若在webpack内配置了optimization, 其中的splitChunks项目的chunks: "all", 会导致导出的容器不可用, 报错为配置的远程js报错, 注释chunks配置使用默认即可正常运行, 原因可能(瞎掰)为chunks: "all"会破坏本地打包的分包, 导致导出的模块匹配不到.

shared字段内提供容器和使用容器内须保持一致.

容器导出和使用是可以同时存在的, 但是这样会导致去中心化, 不利于后续统一维护迭代, 故本项目采取了中心化配置, 多个项目使用的组件统一导出, 各位大佬可根据项目采取不同方式处理.

有关模块联邦的后续使用遇到的问题、总结的经验会不断更新至此文章.