鉴于官网针对通过模块联邦构建微前端应用文档不清晰,所以做的一点补充文章;此文章只针对微前端实现路由拆分,如何通信,如何css拆分可以另起文章介绍
1. 参考资料
官网: module-federation.io/zh/guide/st…
vite-plugin: module-federation.io/zh/guide/ba…
Vue Bridge: module-federation.io/zh/practice…
简单介绍一下其中2个插件的功能,vite-plugin在子应用中负责将我们的组件或者部分应用或者整个应用形成一个js暴露出去供其他模块联邦使用;在父应用中vite-plugin负责导入;Vue Bridge则是一个重新包装vue子应用的工具函数,如果你想导出的组件是单个组件,不涉及内部路由切换,则不需要包装,子应用重新包装后,子应用将具有自己的路由,自己进行内部组件渲染运行挂在,这对于想实现微前端来说很重要
2. 子应用配置
首先在src文件夹下新增一个export-app.ts,如果你的项目原本就是一个结构复杂的项目,想将其快速的拆分成一个个子应用,那么你可以新建一个exports文件夹,里面针对各个子应用各自新建xx-app.ts,快速通过路由快速的划分掉各个子系统;
export-app.ts
import App from './App.vue';
import router from './router';
// 如果原系统改造时只想暴露一部分的路由当做一个单独的子应用
// 在router/index.ts中新创建一个空路由,但是原路由守卫等配置可直接复用
// import { customRouter } from './router';
// customRouter.addRoutes([// ...// ])
import './assets/main.css';
import { createPinia } from 'pinia';
import { createBridgeComponent } from '@module-federation/bridge-vue3';
export default createBridgeComponent({
rootComponent: App,
appOptions: ({ app }) => {
// Optional: adding a plugin to the new Vue instance on the host application side
app.use(createPinia());
return { router };
// return { customRouter };
},
});
vite.config.ts
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import vueDevTools from 'vite-plugin-vue-devtools';
import { federation } from '@module-federation/vite';
// https://vite.dev/config/
export default defineConfig({
server: {
origin: 'http://localhost:5174',
port: 5174,
},
plugins: [
vue(),
vueJsx(),
vueDevTools(),
federation({
name: 'remote1',
filename: 'remoteEntry.js',
exposes: {
'./exportapp': './src/export-app.ts',
// 多个应用时自己添加
// './appA': './src/export-app/xxx-app.ts',
// './appB': './src/export-app/xxx-appB.ts',
},
shared: ['vue', 'vue-router'],
}),
],
build: {
target: 'chrome89',
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
});
3. 主应用配置
vite.config.ts
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import vueDevTools from 'vite-plugin-vue-devtools';
import federation from '@originjs/vite-plugin-federation';
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueJsx(),
vueDevTools(),
federation({
name: 'host',
remotes: {
remote1: import.meta.env.DEV
? 'http://localhost:5174/remoteEntry.js'
: 'http://xxxxx/remoteEntry.js',
// 其他应用打包发布后,替换为服务器地址
},
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
});
router.ts
import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '../views/HomeView.vue';
import { createRemoteComponent } from '@module-federation/bridge-vue3';
const Remote1 = createRemoteComponent({
loader: () => import('remote1/exportapp'),
});
// const RemoteA = createRemoteComponent({
// loader: () => import('remote1/exportappA'),
// });
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
},
{
path: '/about/:pathMatch(.*)*',
name: 'about',
component: Remote1,
},
// {
// path: '/APath/:pathMatch(.*)*',
// name: 'about',
// component: RemoteA,
// },
],
});
export default router;
最终效果
正确的加载了子应用,而且刷新也不会丢失状态