webpack5 Module Federation “微前端”vue版demo使用示例及实现细节
Module Federation:是webpack5新出的一种“微前端”的概念,此文介绍一下具体的实际操作 vue版
demo内关系介绍:
关系:app1对外暴露“微服务”,app2使用app1的“微服务”

上代码
目录结构
.├── README.md├── app1 // app1对外暴露“微服务”│ ├── package.json│ ├── public│ │ └── index.html│ ├── src│ │ ├── App.vue│ │ ├── components│ │ │ └── Header.vue│ │ └── main.js│ └── webpack.config.js└── app2 // app2使用app1的“微服务”├── package.json├── public│ └── index.html├── src│ ├── App.vue│ └── main.js└── webpack.config.js
app1和app2相同部分代码(环境)
- package.json相同
- src/main.js相同
- public/index.html相同
// package.json{"scripts": {"start": "webpack serve","build": "webpack"},"devDependencies": {"vue-loader": "^15.9.3","vue-template-compiler": "^2.6.12","@babel/core": "^7.14.3","babel-loader": "^8.2.2","html-webpack-plugin": "^5.3.1","webpack": "^5.38.1","webpack-cli": "^4.7.2","webpack-dev-server": "^3.11.2"},"dependencies": {"vue": "^2.6.12"}}// src/main.jsimport Vue from "vue";import App from "./App.vue";new Vue({el: '#app',render: h => h(App)})// public/index.html<html><head></head><body><div id="app"></div></body></html>
app1 的组件代码
- App.vue
- components/Header.vue
// App.vue<template><div id="app" style="border: 1px solid cornflowerblue"><h2>我是app.vue</h2><Header name="app1"/></div></template><script>import Header from './components/Header.vue'export default {components: {Header}}</script>// components/Header.vue<template><div style="border: 1px solid olivedrab"><h4>我是header.vue, 这是传参:{{name}}</h4></div></template><script>export default {props: {name: {type: String,default: ''}}}</script>
app2 的组件代码
- App.vue 内 使用了app1的“微服务”
// App.vue<template><div id="app"><Header name="app222222"/> // 此处 app2使用app1的“微服务”<br><qwe></qwe> // 此处 app2使用app1的“微服务”</div></template><script>export default {components: {Header: () => import('app1/Header'), // 此处 app2使用app1的“微服务”qwe: () => import('app1/appIndex') // 此处 app2使用app1的“微服务”}}</script>
实现关键:webpack.config.js
app1的webpack.config.js (对外暴露“微服务”)
const path = require('path');const VueLoaderPlugin = require('vue-loader/lib/plugin')const HTMLWebpackPlugin = require('html-webpack-plugin');const { ModuleFederationPlugin } = require("webpack").container;module.exports = {target: 'web',entry: './src/main.js',mode: "development",devServer: {port: 3000,hot: true,open: true,contentBase: path.join(__dirname, "dist"),},module: {rules: [{test: /.js$/,loader: 'babel-loader',exclude: /node_modules/},{test: /.vue$/,loader: 'vue-loader'}]},plugins: [// 请确保引入这个插件!new VueLoaderPlugin(),new HTMLWebpackPlugin({template: path.resolve(__dirname, './public/index.html')}),new ModuleFederationPlugin({// 提供给其他服务加载的文件filename: "remoteEntry.js",// 唯一ID,用于标记当前服务name: "app1",library: { type: "var", name: "app1" },// 需要暴露的模块,使用时通过 `${name}/${expose}` 引入exposes: {'./Header': "./src/components/Header.vue", // app1对外暴露“微服务”Header(组件)'./appIndex': "./src/App.vue", // app1对外暴露“微服务”appIndex(组件)}})]}
app2的webpack.config.js (接受app1的“微服务”(组件))
const path = require('path');const VueLoaderPlugin = require('vue-loader/lib/plugin')const HTMLWebpackPlugin = require('html-webpack-plugin');const { ModuleFederationPlugin } = require("webpack").container;module.exports = {target: 'web',entry: './src/main.js',mode: "development",devServer: {port: 3001,hot: true,open: true,contentBase: path.join(__dirname, "dist"),},module: {rules: [{test: /.js$/,loader: 'babel-loader',exclude: /node_modules/},{test: /.vue$/,loader: 'vue-loader'}]},plugins: [// 请确保引入这个插件!new VueLoaderPlugin(),new HTMLWebpackPlugin({template: path.resolve(__dirname, './public/index.html')}),new ModuleFederationPlugin({name: "app2",remotes: {app1: "app1@http://localhost:3000/remoteEntry.js", // (接受app1的“微服务”),remoteEntry可以理解为中间代理人)}})]}
效果展示
app1要先npm run start起来
然后app2 才能 npm run start起来

可以看到app2内,可以使用app1的对外暴露的“微服务”(组件)
实现细节
(这里面对比上面的代码,多加了moment.js库依赖)
此图中: 端口3001是当前服务,使用了端口3000的微服务
细节解析:
- 3001服务引入微服务入口 remoteEntry.js,通过remoteEntry.js去获取对应的依赖
- 对应的依赖有:组件和第三方库
- 每一个组件都会是一个独立的文件,然后第三方依赖也会单独拆出来,防止重复打包
- 上图中
- 倒数的两个资源都是用了moment方法的组件,也被3000对外暴露微服务了。此处作为组件引入3001服务内
- 倒数第三个是 第三方依赖 目前主要是moment.js (此处应当按需加载优化,不应该整个moment.js都加载进来了)
另外还做了一个测试:3000的微服务做了变更后,3001服务不用变动,强制刷新就能生效
- 因为,这个是通过域名,获取到对应的资源。相当于直接拉服务器的静态资源
码字不易,点赞鼓励
