在vue项目中使用webpack5的模块联邦
八月更文第三弹, 开始啦! 🍦
这是我参与8月更文挑战的第3天,最近公司要进行微前端技术转型,新的项目准备使用webpack5,之前用的是阿里的qiankun.然后我这边开始尝试用webpack5的模块联邦调试,这个特性应该是从底层为微前端提供了支持。
项目配置
ModuleFederationPlugin
模块联邦本身是一个Webpack 插件 ModuleFederationPlugin,插件有几个重要参数:
name必须,唯一 ID,作为输出的模块名,使用的时通过 {expose} 的方式使用;。remotes可以将其他项目的name映射到当前项目中,远程其他共享模块的加载地址。exposes表示导出的模块,只有在此申明的模块才可以作为远程依赖被使用。shared是非常重要的参数,制定了这个参数,可以让远程加载的模块对应依赖改为使用本地项目。
创建应用app1
- 首先创建vue项目app1,新建
webpack.config.js,进行模块联邦配置(注意VueLoaderPlugin的使用)
const path = require('path');
// vue-loader在15.*之后的版本都是 vue-loader的使用都是需要伴生 VueLoaderPlugin的,
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HTMLWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require("webpack").container;
module.exports = {
devtool: false,
entry: './src/main.js',
mode: "development",
devServer: {
port: 3000,
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: {
'./Button': "./src/components/Button.vue",
}
})
]
}
- 依赖安装 在配置模块联邦时遇到了很多兼容问题,比如babel版本冲突等,demo项目中package.json配置如下
{
"scripts": {
"start": "webpack serve",
"build": "webpack"
},
"devDependencies": {
"@babel/core": "7.9.6",
"babel-loader": "^8.1.0",
"html-webpack-plugin": "^4.5.0",
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.12",
"webpack": "^5.0.0",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"vue": "^2.6.12",
"webpack-cli": "^4.0.0"
}
}
创建应用app2
- 首先创建vue项目app2,新建
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 = {
devtool: false,
entry: './src/main.js',
mode: "development",
devServer: {
port: 3001,
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
{
"scripts": {
"start": "webpack serve",
"build": "webpack"
},
"devDependencies": {
"@babel/core": "7.9.6",
"babel-loader": "^8.1.0",
"html-webpack-plugin": "^4.5.0",
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.12",
"webpack": "^5.0.0",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"vue": "^2.6.12",
"webpack-cli": "^4.0.0"
}
}
在app2中调用app1
webpack配置好之后,我们通过地址访问可以看到http://localhost:3000/remoteEntry.js 中的文件已经被共享,在app2中直接以组件形式进行引用即可。
import("app1/Button") 会被解析大致为:
loadScript("http://localhost:3000/remoteEntry.js").then(() => require('Content'))
<template>
<div id="app">
<h1>我是子应用</h1>
<p>我将使用主应用的共享组件</p>
<ul>
<li>
<Button />
</li>
</ul>
</div>
</template>
<script>
export default {
components: {
Button: () => import("app1/Button"),
},
};
</script>
部署预览
demo项目地址
sample-collection-front-end-projects: 前端开箱即用的demo项目合集 (gitee.com)
总结
模块联邦为大型应用提供了开箱解决方案,有效的解决了模块共享的问题。
本文到此结束,希望对你有帮助 😉