在聊模块联邦之前,我们先了解下在多个项目下,前端模块如何复用的
跨项目模块复用方案
npm 包
包管理工具对于前端应用来说不可或缺,我们可以将模块上传到 npm 包,在需要的项目中引入,以此来复用一些公用模块。
但如果这个 npm 包频繁修改,那么依赖它的项目每次都需要升级版本,编译之后上线
monorepo
monorepo
是一种将多个项目代码存放在一个仓库里的软件开发模式,monorepo 中所有的项目相互独立,但不同项目之间可以相互引用,但如果被依赖的公共项目升级,其他引用它的项目同样也需要升级版本,编译上线
git submodule
git 提供了子模块做代码复用,任何一个git repo 都可以通过引用其它 repo(子模块)来做模块复用,在主项目中通过git commitId
来管理子项目的版本。这就带来一个问题,多人协作中子项目版本容易造成冲突,每次都需要手动更新子模块引用,子模块版本管理很难做
复制粘贴
CV大法 或许是实现复用成本最低的一种方案。虽然复制粘贴能够快速实现代码复用,但是对于未来的长期维护却是灾难性的,代码维护成本会越来越高
UMD
UMD
是 (Universal Module Definition) 通用模块定义的缩写,UMD 将 commonjs、amd 规范集于一身,并支持导出全局变量使用。
UMD
方案存在一些缺陷:其导出的全局变量,容易造成冲突,而且多个 UMD 模块如果相互依赖,那么加载顺序就需要手动去维护,tree shaking
也不是很好做
我们刚才提到的跨项目模块复用的方案中,除了UMD
方案,其它4种,最大的局限是复用组件、项目升级后,需要所有依赖它的项目进行升级、编译、上线,一两个项目这么做可能没什么问题,但是如果是几十个项目,成本会非常高
有没有一种方案,复用模块版本升级,所有依赖它的项目不再需要单独部署上线,在线上环境就能加载到升级之后的模块?
webpack5 提供了模块联邦,解决了上述问题,让代码直接在项目间利用 CDN 直接共享(远程模块),不再需要本地安装 npm 包、构建在发布了
模块联邦概述
下面是官网的介绍:
多个独立的构建可以形成一个应用程序。这些独立的构建不会相互依赖,可以独立开发、部署,这通常被称为微前端,但并不仅限于此。
模块联邦中有几个重要的概念
Remote
作为模块提供者,对外导出模块,被 Host 引用
Host
作为模块消费者,可以动态加载 Remote 提供的模块
Share
Remote、Host可以共享相同的依赖,避免重复打包,减少包体积
下面这张图可以清晰的看到他们三者之间的关系
如何使用
webpack5 提供了 ModuleFederationPlugin
插件来使用模块联邦,下面是插件的配置项
Remote 配置
new ModuleFederationPlugin({
name: "component_app",
filename: "remoteEntry.js",
exposes: {
"./Button":"./src/Button.jsx",
},
shared: ["react", "react-dom"]
})
name
是模块名称,用来标识模块,以确保其它模块能够正确引用该模块
filename
用于指定输出模块的文件名,其它项目通过其加载导出的模块
exposes
用来指定需要导出的模块
shared
用来指定需要共享的依赖模块,如果两个应用都使用了相同的依赖,则可以使用 shared 来共享依赖,减小打包体积
Host 配置
new ModuleFederationPlugin({
name: "main_app",
remotes: {
"component-app":"remote_url"
},
shared: ["react", "react-dom"]
})
关于Host
,重点说一下 remotes
配置
它是用来指定需要从哪个应用加载远程模块,其中 component-app
是一个模块别名, remote_url
是 Remote 导出的 filename
文件地址
在项目中引入共享模块 Button
(以上面 Remote 导出的模块为例,在 Host 项目中使用),由于是异步模块,所以需要使用 import()
来引用
React.lazy(() => import('component-app/Button'))
如果是多个项目,每个项目配置Host
后,即可方便的使用 Remote
导出的模块 Button,这样如果Button 组件修改,我们只需要单独打包 Button 所在的 Remote
应用,重新导出最新的 Button 模块后发布,其它项目中引用的 Button 模块就是最新发布后的代码了,不再需要单独构建、发布了
优点
1、配置简单灵活
2、支持独立部署,上线
3、运行时加载模块,以及共享依赖,减少应用包体积
4、相比 externals 以及 dll
,模块联邦能够更好的做到按需热插拔
缺点
1、如果使用 webpack
, 必须使用 webpack5
2、模块联邦对runtime
运行时做了大量改造,在运行时要做的事情也因此增加很多,会对我们页面的运行时性能造成一定的负面影响
3、由于是运行时共享,那么远程模块的版本管理也是应该去考虑的问题
Vite 同样也支持模块联邦 vite-plugin-federation 原理基本上是一样
参考链接
社区中 webpack4 支持模块联邦的 (github.com/module-fede…)
脱离 webpack 技术栈,支持模块联邦的也有 (github.com/tnfe/hel)