vue-cli 5接入模块联邦 module federation
vue-cli 5接入模块联邦 module federation
- 模块联邦概念
- 实现思路
- 配置
- 遇到的问题:
模块联邦概念
模块联邦由webpack 5
最先推出的,让应用加载远程的代码模块来实现不同的Web应用共享代码片段.模块联邦分为两个角色,一个是生产者,一个是消费者.生产者暴露代码供消费者消费
(用一个不太精准的比喻 这个就是webpack
内置的cdn
)
实现思路
- 首先要先将
vue-cli
升级到5 具体在上一篇 - 针对模块联邦进行配置
配置
我是vue-cli
接入webpack
应用,vue-cli
接vue-cli
用vue-cli
的配置就好了
webpack生产者 webpack.config.js
const { ModuleFederationPlugin } = require("webpack").container;
const path = require("path");
module.exports = {
entry: "./index.js",
mode: "development",
output: {
publicPath: "http://localhost:6780/",
clean: true,
},
devServer: {
static: {
directory: path.join(__dirname, "dist"),
},
compress: true,
port: 6780,
},
optimization: {
splitChunks: false,//splitChunks和mf冲突不能用
},
plugins: [
new ModuleFederationPlugin({
name: "moduleFederationLib",
filename: "remoteEntry.js",
library: { type: "window", name: "moduleFederationLib" },
exposes: {
"./react": "react",
"./react-dom": "react-dom",
'./apiUrl':"./src/utils/apiUrl"
},
}),
],
};
vue-cli生产者 vue.config.js
// vue.config.js
module.exports = {
publicPath: "http://localhost:4567/",
chainWebpack: (config) => {
/* module federation plugin import */
config.optimization.delete("splitChunks");
config
.plugin("module-federation-plugin")
.use(require("webpack").container.ModuleFederationPlugin, [
{
name: "home", // 模块名称(必须唯一)
filename: "remoteEntry.js",//加载的文件名
library: { type: "window", name: "home" },//type:指定如何将远程模块暴露给其他应用 设置成window才能找到
exposes: {
// 对外暴露的组件
"./HelloWorld": "./src/components/HelloWorld.vue",
},
},
]);
},
// devSever 一定要设置跨域 能够跨域是整个mf的基础
devServer: {
port: 4567,
hot: true,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers":
"X-Requested-With, content-type, Authorization",
},
},
};
vue-cli消费者 webpack.config.js
module.exports = {
configureWebpack: {
resolve: {
fallback: {
//禁止webpack在找不到引入的文件的时候用fs模块去查找
//模块联邦的模块就是找不到的 webpack尝试在/src下找 在不到再报错 根本就不到远程
fs: false
}
},
},
chainWebpack: (config) => {
/* module federation plugin import */
config.optimization.delete("splitChunks");//splitChunks和mf冲突不能用
config
.plugin("module-federation-plugin")
.use(require("webpack").container.ModuleFederationPlugin, [
{
name: "app",
remotes: {
// 导入
home: "home@http://localhost:4567/remoteEntry.js",
"moduleFederationLib"://remote模块的module Name是不能带 - 不然会导致导入失败
"moduleFederationLib@http://localhost:6780/remoteEntry.js",
},
},
]);
},
};
消费者使用:
<script>
export default {
name: 'App',
components: {
HelloWorld: () => import('home/HelloWorld')
},
mounted() {
//采用异步导入
import('moduleFederationLib/apiUrl').then(({default: apiUrl}) =>{
console.log('apiUrl!',apiUrl)
})
}
}
</script>
遇到的问题:
问题1.引入远程模块后Uncaught TypeError: Cannot read properties of undefined (reading ‘call’),不引入就没有这个问题
解决方法:
- 检查生产者的remoteEntry.js是否正确启动
- 检查config中library是否已经设置成window,如果成功设置成window在控制台可以检查
问题2.ScriptExternalLoadError: Loading script failed
解决方法:
- 检查splitChunks是否已经设置成false
- 检查生产者的remoteEntry.js是否正确启动
问题3.不能够像webpack
示例一样 使用静态导入远程模块
原因:
mf提供的模块是远程模块,必须要先加载远程模块才能够像静态模块一样使用
解决方法:
使用动态加载远程模块,再加载消费者
注意:import
静态导入的模块会提升至顶层,所以必须使用动态导入
bootstrap.js
//bootstrap.js
import Vue from 'vue';
Vue.config.productionTip = false;
const loadRemoteAndInitApp = async () => {
try {
//先动态导入远程模块
const remote = await import('moduleFederationLib/apiUrl');
console.log('Successfully loaded remote component:', remote);
//导入成功之后再加载App.vue(消费模块的页面) 一定要确保先加载模块再导入消费者
const App = (await import('./App.vue')).default;
//创建并挂载 Vue 实例
new Vue({
render: h => h(App),
}).$mount('#app');
console.log('Vue app has been mounted.');
} catch (error) {
console.error('Error loading remote component or initializing Vue app:', error);
}
};
loadRemoteAndInitApp();
main.js
import './bootstrap'
远程模块消费者App.vue
import APIURl from 'moduleFederationLib/apiUrl'
// 下面就跟正常从文件夹导入就行