1️⃣ 项目背景与挑战
随着业务规模的扩大,传统的单体应用架构逐渐暴露出开发效率低、协作难、部署风险高等问题。为了解决这些痛点,我们团队决定引入微前端架构,并结合模块联邦技术,实现真正意义上的解耦与资源共享。
2️⃣ 技术选型
2.1 核心框架
- Vue 3:主流的前端框架,支持 Composition API
- Vite:极速开发与构建工具
- @micro-zoe/micro-app:微前端框架,基于 Web Components
- @originjs/vite-plugin-federation:Vite 生态下的模块联邦实现
- Pinia:新一代状态管理
- pnpm:高效包管理器
3️⃣ 架构设计
3.1 整体架构
js
admin-micro-app/
├── apps/
│ └── main/ # 主应用
├── micro/
│ ├── modules/ # 联邦模块(共享库/组件)
│ ├── login/ # 登录子应用
│ └── child/ # 业务子应用
└── scripts/ # 自定义脚手架
3.2 模块联邦的实现
共享模块的配置
在micro/modules目录中,我们通过@originjs/vite-plugin-federation插件配置共享模块:
js
// scripts/models/vite/plugins/provider.ts
export const ModuleFederationProviderPlugin = () => {
const exposes = moduleFederationUtils.getExposes();
return federation({
name: 'modules',
filename: 'remoteEntry.js',
exposes
});
};
共享模块的暴露配置通过moduleFederationUtils.getExposes()自动生成,它会扫描micro/modules/src目录下的所有文件,并生成相应的暴露配置:
js
// scripts/utils/module-federation.ts
export const getExposes = () => {
const dir = pathUtils.getMicroModulesDir();
let exposes = moduleFederationUtils.filePathsToExposes(
moduleFederationUtils.resolveCodeFiles(pathUtils.getMicroModulesDir())
);
moduleFederationUtils.createAbsolutePath(exposes, dir);
return exposes;
}
getExposes的返回值大概长这个样子

消费端的配置
主应用和子应用作为消费端,通过以下配置引用共享模块:
js
// scripts/models/vite/plugins/consumer.ts
export const ModuleFederationConsumerPlugin = () => {
const ports = fsx.readJSONSync(pathUtils.getPortsJsonPath());
const external = process.env.NODE_ENV === 'dev'
? `new Promise(resolve=>resolve('http://localhost:${ports['@micro/modules']}/assets/remoteEntry.js'))`
: `new Promise(resolve=>resolve('/v2/micro/modules/assets/remoteEntry.js'))`;
return federation({
name: 'layout',
filename: 'remoteEntry.js',
remotes: {
modules: {
external,
externalType: "promise"
}
}
});
};
消费端配置之后即可在主或子应用里面通过这种形式引入
js
// 引用共享的Vue
import { createApp } from 'modules/vue';
// 引用共享的Vue Router
import { createRouter } from 'modules/vue-router';
// 引用共享的组件
import { SomeComponent } from 'modules/@shared/components';
同时为了提供TS提示的能力需要在当前应用的根目录新建一个tsconfig.json
js
{
"extends": "@shared/tsconfig",
"compilerOptions": {
"paths": {
"modules/*": ["../../micro/modules/src/*"],
"shared/*": ["../../shared/*"],
}
}
}
3.3 微前端的实现
主应用配置
js
// apps/main/src/main.ts
microApp.start({
'router-mode': 'native',
prefetchDelay: 1000,
preFetchApps: MicroUtils.getMicroPreFetch(),
plugins: {}
});
子应用加载
js
<!-- apps/main/src/components/MicroApp/index.vue -->
<template>
<MicroAppDevMode
v-if="envUtils.isDev()"
:app-name="props.appName"
:init-data="props.initData"
@mounted="microAppMounted"
/>
<MicroAppProdMode
v-else
:app-name="props.appName"
:init-data="props.initData"
/>
</template>
4️⃣ 开发流程
4.1 开发环境启动
开发环境启动时,只会启动共享模块服务和主应用:
js
/ scripts/models/commands/dev.ts
async devRoot() {
const modules = ProjectFactory.create(
EProjectType.Provider,
pathUtils.getAppProjectDir('@micro/modules')
);
await modules.microModulePreview();
const mainApp = ProjectFactory.create(
EProjectType.Consumer,
pathUtils.getAppProjectDir('@apps/main')
);
mainApp.serve();
}
在开发环境下,子应用不会在启动时全部加载,而是采用按需加载的方式
子应用启动流程:
js
// apps/main/src/components/MicroApp/MicroAppDevMode.vue
onMounted(() => {
if (!props.appName) return null;
// 检查子应用是否已经启动
if (getAllApps().includes(props.appName)) {
loading.value = false;
return null;
}
// 发送启动子应用的请求
import.meta.hot.send({
type: EAppDevSocketType.StartApp,
data: props.appName
});
// 监听子应用启动完成事件
import.meta.hot.on(EAppDevSocketType.AppStarted, startAppChange);
});
vite写一个服务器插件去监听客户端启动子应用的请求

子应用启动流程总结
- 主应用启动时,只启动共享模块服务
- 当用户访问子应用路由时,触发子应用加载
- 主应用通过 WebSocket 发送启动子应用的请求
- 子应用开发服务器启动,并通知主应用
- 主应用收到通知后,加载子应用组件
- 子应用组件通过 micro-app 加载子应用内容
构建流程
共享模块的构建
js
// scripts/models/vite/configuration/provider-build.ts
export const ProviderBuildConfiguration = () => {
return defineConfig({
plugins: ViteBase.plugins?.concat([
ModuleFederationProviderPlugin(),
topLevelAwait({
promiseExportName: "__tla",
promiseImportName: i => `__tla_${i}`
})
]),
build: {
minify: 'terser',
terserOptions: {
mangle: true,
compress: {
drop_console: true
}
}
}
});
};
主子应用的构建
js
export const ConsumerBuildConfiguration = () => {
return defineConfig({
plugins: ViteBase.plugins?.concat([
RedirectVueImports(),
ModuleFederationConsumerPlugin()
]),
optimizeDeps: {
exclude: ['vue']
},
build: ViteBase.build,
mode: 'production',
css: ViteBase.css,
resolve: ViteBase.resolve
});
};
5️⃣ 实践效果
5.1 开发效率提升
- 开发环境启动时间缩短 70%
- 构建效率提升 50%
- 代码复用率提升
- 团队协作更顺畅
5.2 资源利用优化
- 按需加载减少不必要的资源消耗
- 共享模块减少重复代码
- 构建产物优化,减少包体积
结语
如果想尝试微前端又想把公用资源进行抽离可以尝试一下这套解决方案,具体代码参考github.com/chaorenluo/...