前言
容器和子应用都是vue-cli5搭建的,使用的是vue3+element-plus
host代码
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<div v-if="isLoadingComponent">Loading HelloWorld.vue</div>
<HelloWorld msg="Welcome " />
<el-button>本地测试按钮</el-button>
</div>
</template>
<script lang="ts" setup>
import { defineAsyncComponent, ref } from 'vue';
import lodash from 'lodash';
const isLoadingComponent = ref(true);
const HelloWorld = defineAsyncComponent(() =>
// eslint-disable-next-line
import('app_exposes/HelloWorld.vue').finally(() => {
console.log('finally');
isLoadingComponent.value = false;
}));
</script>
host配置
const { defineConfig } = require("@vue/cli-service");
const webpack = require("webpack");
const pkg = require("./package.json");
const appName = pkg.name;
const publicPath = `/${appName}`;
module.exports = defineConfig({
publicPath,
pages: {
index: {
entry: './src/main.ts',
},
},
devServer: {
// 关闭主机检查,使微应用可以被 fetch
allowedHosts: "*", // webpack@4设置 disableHostCheck: true
// 配置跨域请求头,解决开发环境的跨域问题
headers: {
"Access-Control-Allow-Origin": "*",
},
},
configureWebpack: {
experiments: {
topLevelAwait: true,
},
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'async',
reuseExistingChunk: true,
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'async',
reuseExistingChunk: true,
},
},
},
},
output: {
library: appName,
libraryTarget: "umd",
chunkLoadingGlobal: `webpackJsonp_${appName}`, // webpack@4设置 jsonpFunction: `webpackJsonp_${appName}`
},
plugins: [
new webpack.container.ModuleFederationPlugin({
name: "app_general",
filename: "remote.js",
remotes: {
app_exposes: "app_exposes@http://localhost:5552/remoteEntry.js",
},
shared: {
vue: { singleton: true },
},
}),
],
},
transpileDependencies: true,
});
remote配置
const { defineConfig } = require("@vue/cli-service");
const webpack = require("webpack");
const pkg = require("./package.json");
const appName = pkg.name;
// const publicPath = '/' + appName;
const publicPath = "auto";
module.exports = defineConfig({
publicPath,
pages: {
index: {
entry: './src/main.ts',
},
},
devServer: {
// 关闭主机检查,使微应用可以被 fetch
allowedHosts: "*", // webpack@4设置 disableHostCheck: true
// 配置跨域请求头,解决开发环境的跨域问题
headers: {
"Access-Control-Allow-Origin": "*",
},
},
configureWebpack: {
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'async',
reuseExistingChunk: true,
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'async',
reuseExistingChunk: true,
},
},
},
},
experiments: {
topLevelAwait: true,
},
output: {
library: appName,
libraryTarget: "umd",
chunkLoadingGlobal: `webpackJsonp_${appName}`, // webpack@4设置 jsonpFunction: `webpackJsonp_${appName}`
},
plugins: [
new webpack.container.ModuleFederationPlugin({
name: "app_exposes",
filename: "remoteEntry.js",
library: { type: "umd", name: "app_exposes" },
exposes: {
"./HelloWorld.vue": "./src/components/HelloWorld.vue",
"./AboutView.vue": "./src/views/AboutView.vue",
"./helloWord": "./src/components/helloWord.js",
},
shared: {
vue: { singleton: true },
lodash: { singleton: true, eager: true },
// 其他共享的依赖模块
},
}),
],
},
transpileDependencies: true,
});
遇到的bug
- host应用加载remote应用一直报错404 (先单独跑两个子应用)
解决:
// 给remote配置
publicPath:'auto' (设置别的路径,在引入的时候依旧报错404 待解决)
//host配置
const pkg = require("./package.json");
const appName = pkg.name;
const publicPath = `/${appName}`;
module.exports = defineConfig({
publicPath,
}
- host引入element组件样式失败
解决方法host和remote都配置,remote共享自己的vue
shared: { vue: { singleton: true }, },
shared: { // 模块名称或路径 moduleName: { // 是否将模块共享为单例模式 singleton: true, // 共享模块的版本要求 requiredVersion: '^1.0.0', // 是否使用严格的版本匹配(必需满足指定的版本) strictVersion: true, // 是否在主应用程序加载前立即加载共享模块 eager: true, // 是否允许共享模块在多个实例中共存 // 默认情况下,模块联邦会尝试合并同一个名称的模块 // 如果设置为 true,则每个应用程序将独立加载自己的实例 shareKey: 'dependency@1.0.0', // 共享模块的名称的别名,用于在引入模块时使用 import: 'dependency-alias', // 共享模块的版本范围,只有一个共享模块匹配该范围时才能加载 // 例如,"dependency@^1.0.0" requiredVersion: '^1.0.0', // 是否禁用共享模块,即不将其提供给其他应用程序 // 默认情况下,所有共享的模块都是可用的 // 如果设置为 false,则禁用该共享模块 eager: false, // 自定义共享模块的初始化逻辑 // 使用 shared module 的初始化函数替代默认的初始化逻辑 init: 'module-init-function', // 手动指定分享模块的提供者 // 默认情况下,模块联邦会自动检测分享模块的提供者 // 通过指定这个选项,可以手动设置共享模块的提供者 // 例如,provider: () => require('module-provider') provider: 'module-provider', // 是否允许共享模块在 fallback 中被选择作为备选项 // 默认情况下禁止备用共享模块为提供者的选择 fallback: true, // 是否使用 shared module 的引导逻辑 // 默认情况下,模块联邦会为每个共享模块生成自动的引导逻辑 // 如果设置为 false,将禁用自动生成的引导逻辑 singleton: false, // 用于生成 shared module 引导逻辑的文件名称 // 默认为 'remoteEntry.js' filename: 'shared-module-remoteEntry.js', }, // ... 其他模块配置 }
- shared vue 引入报错
参考webpack官网解答webpack.docschina.org/concepts/mo… 需要host和remote异步引入vue
首先在host和remote创建init.ts
import { createApp } from 'vue';
import ElementPlus from 'element-plus';
import locale from 'element-plus/lib/locale/lang/zh-cn';
import App from './App.vue';
import router from './router';
import 'element-plus/dist/index.css';
let app: any = null;
function render(props: any) {
const { container } = props;
app = createApp(App);
app.use(router)
.use(ElementPlus, { locale })
.mount(container ? container.querySelector('#app') : '#app');
}
if (!(window as any).__POWERED_BY_QIANKUN__) {
render({});
}
async function bootstrap() {
console.log('bootstrap');
}
async function mount(props: any) {
console.log('mount-general', props);
render(props);
}
async function unmount() {
console.log('unmount-general');
app.unmount();
}
function update() {
console.log('update-general');
}
export {
render,
app,
mount,
update,
unmount,
bootstrap,
}
import './public-path';
const {
mount,
unmount,
bootstrap,
update,
} = await import('./init');
export {
update,
mount,
unmount,
bootstrap,
}
异步导出报错找不到生命周期
- 异步导出插件 安装@babel/plugin-syntax-top-level-await 并且配置vue.config.js
configureWebpack: {
experiments: {
topLevelAwait: true,
},
}
experiments配置选项用于启用Webpack中的实验性功能。其中,topLevelAwait是experiments配置选项的一个可用设置,用于启用顶层的异步等待(Top-level Await)语法支持。在Webpack中,顶层异步等待是指在模块的顶层作用域中使用
await关键字等待一个Promise的执行结果。这样做可以简化异步编程,使代码更加直观和易读。