基于webpack 5 ModuleFederationPlugin 实现微前端
有这么一个需求,项目里有很多业务模块,它们都有引用一些公共组件,每个业务模块打包后都是一个独立的应用,当公共组件修改时,单独打包公共组件,其他应用能够不需要重新构建,就能直接使用最新的公共组件,要怎么实现?
一开始我想到的是使用网络资源,就是把公共组件打包后的js文件放到服务器,其他应用通过script请求使用,尝试了一下,难以实现,放弃了。
后来找到了webpack 5的ModuleFederationPlugin,看了一下官方文档,完全适合这个需求,记录一下实现过程。
主要使用webpack 5的ModuleFederationPlugin插件实现模块共享和引用。
一、先创建两个vue-cli项目,分别是app1和app2
app1作为公共应用,把组件共享给其他应用使用
app2作为业务应用,引用app1共享的组件
这里就不介绍vue-cli创建步骤了,直接跳过
二、app1新增组件button.vue和divider.vue
index.js提供按需引用
// button/index.js import Button from './button.vue' export default { install(Vue) { Vue.component('PtButton', Button) } }
common.js提供全量注册
// common.js import './style.css' import Button from './button/index' import Divider from './divider/index' const install = async (vue) => { [ Button, Divider ].forEach(component => { vue.use(component) }) } export default { install, Button, Divider }
vue.config.js 配置
// vue.config.js module.exports = { // 共享模块必须是完整路径,不能是相对路径,否则无法请求额外的css和js资源
publicPath: process.env.NODE_ENV === 'production' ? "http://localhost:2020/peento" : "http://localhost:8095", chainWebpack: (config) => { /* splitChunks可能会和 ModuleFederationPlugin 造成冲突,先移除 */ config.optimization.delete("splitChunks"); config .plugin("module-federation-plugin") .use(require("webpack").container.ModuleFederationPlugin, [ { name: "peento", // 模块名称 filename: "peento.js", // 输出的文件名称 exposes: { // 对外暴露的组件 "./peento-ui": "./src/components/common.js", // 暴露所有组件,通过use方法使用 "./button": "./src/components/button/button", // 暴露单独组件,需要注册使用 }, remotes: {} }, ]); }, devServer: { port: 8095, hot: true }, };
三、app2引用app1共享出来的组件
app2的vue.config.js配置
// app2 vue.config.js module.exports = { chainWebpack(config) { config.optimization.delete('splitChunks') config .plugin("module-federation-plugin") .use(require("webpack").container.ModuleFederationPlugin, [ { remotes: { // 生产环境所有应用域名相同的情况下,可以使用相对路径,否则需要使用完整路径 peento: process.env.NODE_ENV === 'production' ? "peento@http://localhost:2020/peento/peento.js" : "peento@http://localhost:8095/peento.js" } } ]) }, devServer: { port: 8085, hot: true }, };
app2的mian.js引用app1的组件
// app2 main.js import Vue from 'vue' import App from './App.vue' import('peento/peento-ui').then(res => { const peento = res.default Vue.use(peento) // 全量注册,可直接在组件里使用 new Vue({ render: h => h(App), }).$mount('#app') })
也可以直接在组件中使用
// app2 App.vue <template> <div id="app"> <PtButton>895621</PtButton> </div> </template> <script> export default { name: 'App', components: { PtButton: () => import('peento/button') } } </script>
四、分布运行app1和app2开发环境,app2页面正常显示app1的button组件,实践成功!
下面是我根据上面的需求实现的一个多模块项目,有兴趣的可以看一下;
点击查看:vue-cli-module
项目结构和配置展示: