🚀 微信小程序微前端架构白皮书:@dd-code/uni-tools
核心理念: 物理隔离 (Physical Isolation)、独立运行 (Independent Runtime)、运行时聚合 (Runtime Aggregation)
适用场景: 大型微信小程序、多团队并行开发、巨石应用治理 (Monolith Governance)
资源链接:
-
背景:大型小程序工程的协作困境
在多团队并行开发的大型业务场景中,传统的单体小程序架构逐渐面临严峻的工程化挑战。当代码规模增长至 50 万行以上,页面数量超过 500 个时,开发团队不仅遭遇性能瓶颈,更面临协作效率与系统可维护性的双重压力。
-
开发效率瓶颈
- 构建速度线性下降:随着文件数量增加,构建工具(Webpack/Vite)的模块解析与依赖分析时间呈指数级增长,冷启动耗时可达 3-5 分钟
- 热更新响应迟缓:局部样式修改需等待 10 秒以上才能在模拟器生效,严重打断开发心流
-
架构质量风险
- 模块 耦合 严重:不同业务模块间存在隐式依赖,如订单模块直接引用用户模块的内部实现,导致重构波及范围不可控
- 协作边界模糊:多团队并行开发时,代码冲突频繁,功能交付相互阻塞
-
三方生态协作风险
- 代码安全边界缺失:外部合作方需接入时,不得不获取完整代码仓库权限,存在核心业务逻辑泄露风险
- 协作 流程 耦合:第三方团队的开发节奏、技术选型、发版周期与主团队强绑定,任何一方的阻塞都会影响整体交付
- 知识传递成本高:向合作方交接需要理解整个单体应用的架构,上手成本巨大,且后期维护界限模糊
-
架构设计:基于 Vite 的微前端解决方案
-
核心设计思路
通过微信小程序的 subpackages机制实现物理分包,结合 CDN 分发与动态配置融合,构建模块化的小程序应用架构。主应用作为基座管理公共资源与运行时,子应用独立开发、独立构建、独立部署。
-
大体流程图
-
设计特点
- 中心化状态管理:所有配置和数据统一存储在
currentManifestJson中,保证一致性 - 插件化架构:每个功能模块都是独立的Vite插件,易于扩展和维护
- 模块联邦支持:通过
exposes插件实现模块间的动态引用和复用 - 多级缓存策略:CDN优先,本地缓存兜底,提升构建速度和稳定性
- 实时联调能力:基于WebSocket的实时通信,支持开发时热更新和同步
- 独立构建部署:子应用可独立开发、构建和部署,支持增量更新
- 目录隔离体系:多级输出目录设计,防止资源冲突,便于管理
-
目录结构设计
采用 Monorepo 组织方式,确保代码物理边界清晰,便于独立开发与团队协作。
root/
├── main-app/ # 主应用(基座):TabBar、公共组件、登录态管理
│ ├── src/
│ ├── vite.config.ts
│ ├── mfe.json # 微前端配置文件
│ └── dist/
├── apps/ # 业务子应用集合
│ ├── module_bwzb/ # 门店业务子应用
│ ├── sub-order/ # 订单业务子应用
│ └── sub-user/ # 用户中心子应用
├── node_modules/
│ └── @dd-code/
│ └── uni-files/ # 远程子应用构建产物缓存目录
├── vite.config.ts # 统一构建配置
└── package.json
核心优势:
- 独立启动:
npm run dev:store仅启动门店业务,零噪声依赖,启动时间 < 1s - 独立构建:每个子应用产出独立的分包目录,构建过程完全解耦
- 独立发布:支持子应用单独构建并上传 CDN,主应用按需更新依赖版本
-
公共模块通信机制
主应用在 vite.config.ts中声明共享模块:
export default defineConfig({
plugins: [
createMpWeixinUniPlugin({
exposes: {
'.': 'src/store/index.ts',
'./axios': 'src/utils/request.ts',
'./user': 'src/store/user.ts'
}
})
]
})
子应用通过统一路径导入共享模块:
import { axios } from '@dd-code/runtime/axios'
import store from '@dd-code/runtime/store'
4. ## 开发模式
受小程序沙箱环境限制(禁止动态执行代码),采用“编译时聚合+运行时发现”的混合策略。
-
独立开发模式
适用于专注开发单个子应用的场景。
- 依赖拉取:构建时读取配置,获取主应用及其他子应用的资源清单
- 缓存校验:比对本地
node_modules缓存与远程 CDN 的文件 Hash - 资源同步:如有更新,自动从 CDN 拉取最新产物至缓存目录
- 环境组装:将主应用及其他子应用产物同步到当前开发的
dist目录 - 结果:获得包含完整依赖的、可直接运行的小程序开发环境
-
联调开发模式
适用于跨应用联调或多模块并行开发场景。
- 主应用服务启动:运行
npm run dev:main,启动 HTTP/WebSocket 服务(默认端口 3561) - 子应用服务接入:运行
npm run dev:sub,子应用向主应用服务发起握手请求 - 输出重定向:子应用将构建产物直接输出到主应用的
dist目录 - 动态配置更新:主应用监听产物变更,自动更新
app.json中的分包配置 - 热插拔体验:开发者获得“修改即生效”的联调体验
-
实现原理:插件化构建流水线
-
整体 流程
微前端构建流程通过插件链实现,createMpWeixinUniPlugin初始化共享的 ManifestManager上下文,并按顺序注册五个核心插件。
-
模块介绍
状态管理中心模块
| 模块名称 | 标识符 | 主要职责 | 颜色编码 |
|---|---|---|---|
| 状态管理中心 | currentManifestJson | 统一存储和管理构建过程中的所有状态数据,包括环境配置、页面JSON、文件清单、模块映射和依赖关系 | 紫色 |
所有插件共享同一个 manifestJson状态对象,作为构建时的内存缓存和最终产出的配置源。
export interface IManifestJson {
code: string; // 小程序唯一标识
appCode: string; // 当前应用标识 (如 'module/bwzb')
mode: 'dev' | 'prod'; // 构建模式
hash: string; // 内容指纹 (SHA256),用于增量更新
files: IFileRow[]; // 产物文件列表
pagesJson: any; // 路由配置,用于主应用路由合并
exposes: Record<string, IExposeInfo>; // 导出模块映射(仅主应用)
cdn: string; // CDN 根路径
publicPath: string; // 资源公共路径
}
构建插件模块
| 插件名称 | 插件标志符 | 主要功能 | 颜色编码 |
|---|---|---|---|
| 环境参数插件 | @dd-code:genre-params | 解析环境变量,初始化输出目录,清理发布目录 | 蓝色 |
| 资源同步与依赖注册插件 | createAppsAssetsPlugin | 下载依赖应用的资源,搬运到指定目录 | 蓝色 |
| 运行时共享插件 | createExposesPlugin | 处理模块联邦配置,收集暴露模块映射 | 蓝色 |
| 产物与路由采集插件 | createManifestPlugin) | 采集页面配置和产物清单,生成manifest文件 | 蓝色 |
| 应用聚合与联调服务插件 | createMainAppPlugin | 启动联调服务,合并子应用配置,处理增量更新 | 蓝色 |
-
环境参数插件 (
@dd-code:genre-params)
- 初始化
ManifestManager环境信息(模式、CDN 地址、应用标识) - 记录输出目录和构建根路径
- 清理发布目录 (
dist/publish),防止旧产物污染
-
资源同步与依赖注册插件 (
createAppsAssetsPlugin)
在构建开始前确保所有依赖的子应用资源就位。
export const createAppsAssetsPlugin = (manifestJson) => ({
name: "@dd-code:apps-assets",
async options() {
// 1. 基于 Hash 校验的并发下载
const manifestList = await downloadFullApps(manifestJson.value);
// 2. 注册依赖关系到上下文
manifestJson.setDependencies(manifestList);
},
buildStart() {
// 3. 移动资源到输出目录(构建模式下跳过子应用自身)
if (checkIsBuildInChild() && !manifestJson.value.isRoot) return;
moveOtherApps({ base: basePath, manifestList });
}
});
关键技术点:
- 智能缓存:利用
node_modules/@dd-code/uni-files作为持久化缓存目录 - 并发下载:使用
Promise.all并行拉取多个子应用资源 - 进度反馈:集成
CliProgressManager提供可视化下载进度 - 过期检测:通过
checkDownloadFilesIsExpired确保缓存有效性
-
产物与路由采集插件 (
createManifestPlugin)
解决 Vite 插件无法感知 uni-app 静态资源复制的问题。
方案:通过 AOP 切面编程劫持文件观察者。
// 劫持 uni-cli-shared 的文件复制行为
FileWatcher.prototype.copy = function (from) {
// 前置钩子:记录文件到 Manifest
before?.(from, to);
const result = originalCopy.call(this, from);
return result;
};
采集 流程:
- 通过
addUniCopyPluginHook注入复制行为拦截器 - 在
writeBundle阶段收集所有 JS/CSS 模块 - 结合拦截器捕获的静态资源,生成完整的文件指纹列表
- 生成
mfe-uni-manifest.json作为版本凭证
-
运行时共享插件 (
createExposesPlugin)
针对小程序不支持动态加载的限制,采用静态 AST 分析 + 路径重写方案。
重写原理:
const renderRuntimeCode = (moduleExpose: IExposeInfo, appCode: string) => {
// 计算相对路径:从子应用构建目录 -> 主应用根目录
const deepPath = path.relative(
path.join(appCode, path.dirname(buildName)),
"."
);
let resultCode = "";
const { exports: exportName = [], path: filePath } = moduleExpose || {};
// 生成桥接代码
exportName.forEach((item) => {
resultCode += `export const {${item}} = require('${deepPath}/${filePath}');`;
});
return resultCode;
};
关键机制:
- 导入语句拦截:识别符合
@dd-code/runtime模式的导入 - 路径映射解析:查询主应用
manifest.json中的exposes配置 - 相对路径计算:动态计算子应用到主应用的相对路径
- 桥接文件生成:显式
emitFile生成物理桥接文件,满足小程序编译器的静态检查
-
应用聚合与联调服务插件 (
createMainAppPlugin)
- 开发模式下主应用启动监听服务
- 独立构建时自动合并
app.json配置 - 联调模式下监听子应用变更,动态更新路由配置
-
存储系统模块
| 存储类型 | 路径/标识 | 用途说明 | 颜色编码 |
|---|---|---|---|
| 本地缓存 | node_modules/SAVE_CDN_FILE_PATH | 存储从CDN下载的资源,作为兜底缓存 | 灰色 |
| 远程资源 | CDN/manifest.json | 存储远程配置和资源文件,支持动态更新 | 灰色 |
| 发布目录 | PUBLISH_PATH | 最终产物发布目录,包含所有构建结果 | 灰色 |
-
运行时服务模块
| 服务类型 | 主要功能 | 交互对象 | 颜色编码 |
|---|---|---|---|
| WebSocket Server | 提供实时通信服务,支持热更新 | 子应用的WS客户端 | 绿色 |
| HTTP Server | 提供静态资源服务,支持资源访问 | 子应用的Vite构建 | 绿色 |
| 配置合并服务 | 合并子应用的pages.json,生成最终的app.json | 主应用插件 | 绿色 |
-
子应用模块
| 模块组件 | 主要功能 | 交互方式 | 颜色编码 |
|---|---|---|---|
| Vite构建 | 独立编译子应用代码,生成产物 | 通过HTTP访问资源 | 橙色 |
| WebSocket客户端 | 连接主应用的WS Server,实现实时同步 | 双向WebSocket通信 | 橙色 |
| 文件监听器 | 监控本地文件变化,上报增量更新 | 向主应用插件发送变更事件 | 橙色 |
-
目录体系模块
| 目录变量 | 含义说明 | 设置方式 |
|---|---|---|
| UNI_OUTPUT_DIR | 主输出根目录,存储所有应用产物 | 由环境参数插件推导设置 |
| MFE_SOURCE_OUTPUT_DIR | 当前构建的输出目录 | 由环境参数插件推导设置 |
| MFE_ROOT_OUTPUT_DIR | 主根推导目录,用于路径解析 | 由环境参数插件推导设置 |
-
模块交互方式
-
构建插件 → 状态管理中心(写入操作)
| 插件 | 写入操作 | 写入数据 | 交互说明 |
|---|---|---|---|
| 环境参数插件 | setEnv | 环境变量配置、输出目录设置 | 初始化构建环境,重置输出目录 |
| 应用资源插件 | setDependencies | 依赖应用信息、资源清单 | 设置依赖关系,管理资源映射 |
| 模块暴露插件 | setExposes | 模块暴露映射、联邦配置 | 配置模块联邦,建立模块间引用关系 |
| 清单管理插件 | setPagesJson/setFiles | 页面配置、文件清单 | 采集构建产物信息,生成完整清单 |
-
状态管理中心 → 主应用插件(读取操作)
| 读取方 | 读取数据 | 用途说明 |
|---|---|---|
| 主应用插件 | 所有整合后的配置数据 | 基于完整状态启动服务、合并配置、处理交互 |
-
构建插件 ↔ 存储系统(资源读写)
| 插件 | 存储目标 | 操作类型 | 具体操作 |
|---|---|---|---|
| 应用资源插件 | 本地缓存 | 写入 | 下载依赖应用并缓存到node_modules |
| 应用资源插件 | 主输出目录 | 写入 | 搬运资源到UNI_OUTPUT_DIR |
| 模块暴露插件 | 远程资源 | 读取 | 从CDN读取根应用的manifest.json |
| 模块暴露插件 | 本地缓存 | 读取 | 兜底读取本地缓存的配置 |
| 清单管理插件 | 发布目录 | 写入 | 写入Manifest文件和拷贝产物到PUBLISH_PATH |
-
主应用插件 ↔ 运行时服务(服务启动)
| 服务 | 启动方式 | 交互说明 |
|---|---|---|
| WebSocket Server | 主应用插件启动 | 建立实时通信通道,支持热更新 |
| HTTP Server | 主应用插件启动 | 提供静态资源服务,支持子应用访问 |
| 配置合并服务 | 主应用插件触发 | 合并所有子应用的pages.json生成最终的app.json |
-
主应用插件 ↔ 子应用(双向交互)
| 交互方向 | 交互组件 | 交互内容 | 通信方式 |
|---|---|---|---|
| 主应用 → 子应用 | 主应用插件 → WS客户端 | 建立连接、监听变更、初始化同步 | WebSocket |
| 子应用 → 主应用 | 文件监听器 → 主应用插件 | 文件变更上报、增量更新通知 | 事件上报 |
-
运行时服务 ↔ 子应用(运行时通信)
| 服务 | 子应用组件 | 通信内容 | 通信协议 |
|---|---|---|---|
| WebSocket Server | WebSocket客户端 | 双向实时通信、状态同步、热更新推送 | WebSocket |
| HTTP Server | Vite构建 | 静态资源访问、模块加载、API调用 | HTTP/HTTPS |
-
子应用接入
4.1 主应用配置文件 (mfe.json)
{
"apps": [
{
"code": "module_bwzb",
"name": "门店管理",
"entry": "apps/module_bwzb"
},
{
"code": "sub-order",
"name": "订单中心",
"entry": "apps/sub-order"
}
]
}
4.2 环境变量配置
| 变量名 | 类型 | 必填 | 描述 |
|---|---|---|---|
| MFE_UNI_IS_ROOT | boolean | 否 | 是否为主应用,主应用必须为 true |
| MFE_UNI_CODE | string | 是 | 小程序唯一标识 |
| MFE_APP_CODE | string | 是 | 子应用唯一标识 |
| MFE_CDN_HOST | string | 是 | CDN 服务地址 |
4.3 Vite 插件配置
import { defineConfig } from "vite";
import uni from "@dcloudio/vite-plugin-uni";
import { createMpWeixinUniPlugin } from "@dd-code/uni-tools";
export default defineConfig({
plugins: [
uni(),
createMpWeixinUniPlugin({
// 模块导出配置(仅主应用需要)
exposes: {
// 默认导出
'.': 'src/store/index.ts',
// 命名导出
'./axios': 'src/utils/request.ts',
'./user': 'src/store/user.ts'
}
}),
],
// 其他配置...
});
5 后续演进规划
-
近期优化
- 全局变量隔离:实现
uni全局对象的多实例隔离,避免跨应用污染 - 路由跳转劫持:统一管理子应用间的路由跳转,增强可控性
- 依赖版本统一:确保三方包版本一致性,避免多版本冲突
-
中期扩展
- H5 微 前端 接入:扩展架构支持 H5 应用集成
- 构建性能优化:探索并行构建、增量编译等加速方案
- 监控体系完善:集成构建监控、运行时性能分析
-
长期愿景
- 低代码集成:支持可视化模块编排
- Serverless 部署:实现子应用独立部署、动态更新
- 跨端统一:扩展支持支付宝、抖音等小程序平台