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.js
import 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服务不用变动,强制刷新就能生效
- 因为,这个是通过域名,获取到对应的资源。相当于直接拉服务器的静态资源
码字不易,点赞鼓励