🚀 搞定 UniApp 小程序微前端:基于 Vite 的主子应用工程化实践

121 阅读13分钟

🚀 微信小程序微前端架构白皮书:@dd-code/uni-tools

核心理念: 物理隔离 (Physical Isolation)、独立运行 (Independent Runtime)、运行时聚合 (Runtime Aggregation)

适用场景: 大型微信小程序、多团队并行开发、巨石应用治理 (Monolith Governance)

资源链接:


  1. 背景:大型小程序工程的协作困境

在多团队并行开发的大型业务场景中,传统的单体小程序架构逐渐面临严峻的工程化挑战。当代码规模增长至 50 万行以上,页面数量超过 500 个时,开发团队不仅遭遇性能瓶颈,更面临协作效率与系统可维护性的双重压力。

  1. 开发效率瓶颈

  • 构建速度线性下降:随着文件数量增加,构建工具(Webpack/Vite)的模块解析与依赖分析时间呈指数级增长,冷启动耗时可达 3-5 分钟
  • 热更新响应迟缓:局部样式修改需等待 10 秒以上才能在模拟器生效,严重打断开发心流
  1. 架构质量风险

  • 模块 耦合 严重:不同业务模块间存在隐式依赖,如订单模块直接引用用户模块的内部实现,导致重构波及范围不可控
  • 协作边界模糊:多团队并行开发时,代码冲突频繁,功能交付相互阻塞
  1. 三方生态协作风险

  • 代码安全边界缺失:外部合作方需接入时,不得不获取完整代码仓库权限,存在核心业务逻辑泄露风险
  • 协作 流程 耦合:第三方团队的开发节奏、技术选型、发版周期与主团队强绑定,任何一方的阻塞都会影响整体交付
  • 知识传递成本高:向合作方交接需要理解整个单体应用的架构,上手成本巨大,且后期维护界限模糊
  1. 架构设计:基于 Vite 的微前端解决方案

  1. 核心设计思路

通过微信小程序的 subpackages机制实现物理分包,结合 CDN 分发与动态配置融合,构建模块化的小程序应用架构。主应用作为基座管理公共资源与运行时,子应用独立开发、独立构建、独立部署。

  1. 大体流程图

image.png

  1. 设计特点

  1. 中心化状态管理:所有配置和数据统一存储在currentManifestJson中,保证一致性
  2. 插件化架构:每个功能模块都是独立的Vite插件,易于扩展和维护
  3. 模块联邦支持:通过exposes插件实现模块间的动态引用和复用
  4. 多级缓存策略:CDN优先,本地缓存兜底,提升构建速度和稳定性
  5. 实时联调能力:基于WebSocket的实时通信,支持开发时热更新和同步
  6. 独立构建部署:子应用可独立开发、构建和部署,支持增量更新
  7. 目录隔离体系:多级输出目录设计,防止资源冲突,便于管理
  1. 目录结构设计

采用 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,主应用按需更新依赖版本
  1. 公共模块通信机制

主应用在 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. ## 开发模式

受小程序沙箱环境限制(禁止动态执行代码),采用“编译时聚合+运行时发现”的混合策略。

  1. 独立开发模式

适用于专注开发单个子应用的场景。

  1. 依赖拉取:构建时读取配置,获取主应用及其他子应用的资源清单
  2. 缓存校验:比对本地 node_modules缓存与远程 CDN 的文件 Hash
  3. 资源同步:如有更新,自动从 CDN 拉取最新产物至缓存目录
  4. 环境组装:将主应用及其他子应用产物同步到当前开发的 dist目录
  5. 结果:获得包含完整依赖的、可直接运行的小程序开发环境
  1. 联调开发模式

适用于跨应用联调或多模块并行开发场景。

  1. 主应用服务启动:运行 npm run dev:main,启动 HTTP/WebSocket 服务(默认端口 3561)
  2. 子应用服务接入:运行 npm run dev:sub,子应用向主应用服务发起握手请求
  3. 输出重定向:子应用将构建产物直接输出到主应用的 dist目录
  4. 动态配置更新:主应用监听产物变更,自动更新 app.json中的分包配置
  5. 热插拔体验:开发者获得“修改即生效”的联调体验
  1. 实现原理:插件化构建流水线

  1. 整体 流程

微前端构建流程通过插件链实现,createMpWeixinUniPlugin初始化共享的 ManifestManager上下文,并按顺序注册五个核心插件。

image.png

  1. 模块介绍

状态管理中心模块

模块名称标识符主要职责颜色编码
状态管理中心​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启动联调服务,合并子应用配置,处理增量更新蓝色
  1. 环境参数插件 (@dd-code:genre-params)
  • 初始化 ManifestManager环境信息(模式、CDN 地址、应用标识)
  • 记录输出目录和构建根路径
  • 清理发布目录 (dist/publish),防止旧产物污染
  1. 资源同步与依赖注册插件 (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确保缓存有效性
  1. 产物与路由采集插件 (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;
};

采集 流程

  1. 通过 addUniCopyPluginHook注入复制行为拦截器
  2. writeBundle阶段收集所有 JS/CSS 模块
  3. 结合拦截器捕获的静态资源,生成完整的文件指纹列表
  4. 生成 mfe-uni-manifest.json作为版本凭证
  1. 运行时共享插件 (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;
};

关键机制

  1. 导入语句拦截:识别符合 @dd-code/runtime模式的导入
  2. 路径映射解析:查询主应用 manifest.json中的 exposes配置
  3. 相对路径计算:动态计算子应用到主应用的相对路径
  4. 桥接文件生成:显式 emitFile生成物理桥接文件,满足小程序编译器的静态检查
  1. 应用聚合与联调服务插件 (createMainAppPlugin)
  • 开发模式下主应用启动监听服务
  • 独立构建时自动合并 app.json配置
  • 联调模式下监听子应用变更,动态更新路由配置
  1. 存储系统模块

存储类型路径/标识用途说明颜色编码
本地缓存​node_modules/SAVE_CDN_FILE_PATH存储从CDN下载的资源,作为兜底缓存灰色
远程资源​CDN/manifest.json存储远程配置和资源文件,支持动态更新灰色
发布目录​PUBLISH_PATH最终产物发布目录,包含所有构建结果灰色
  1. 运行时服务模块

服务类型主要功能交互对象颜色编码
WebSocket Server​提供实时通信服务,支持热更新子应用的WS客户端绿色
HTTP Server​提供静态资源服务,支持资源访问子应用的Vite构建绿色
配置合并服务​合并子应用的pages.json,生成最终的app.json主应用插件绿色
  1. 子应用模块

模块组件主要功能交互方式颜色编码
Vite构建​独立编译子应用代码,生成产物通过HTTP访问资源橙色
WebSocket客户端​连接主应用的WS Server,实现实时同步双向WebSocket通信橙色
文件监听器​监控本地文件变化,上报增量更新向主应用插件发送变更事件橙色
  1. 目录体系模块

目录变量含义说明设置方式
UNI_OUTPUT_DIR主输出根目录,存储所有应用产物由环境参数插件推导设置
MFE_SOURCE_OUTPUT_DIR当前构建的输出目录由环境参数插件推导设置
MFE_ROOT_OUTPUT_DIR主根推导目录,用于路径解析由环境参数插件推导设置
  1. 模块交互方式

  1. 构建插件 → 状态管理中心(写入操作)

插件写入操作写入数据交互说明
环境参数插件​setEnv环境变量配置、输出目录设置初始化构建环境,重置输出目录
应用资源插件​setDependencies依赖应用信息、资源清单设置依赖关系,管理资源映射
模块暴露插件​setExposes模块暴露映射、联邦配置配置模块联邦,建立模块间引用关系
清单管理插件​setPagesJson/setFiles页面配置、文件清单采集构建产物信息,生成完整清单
  1. 状态管理中心 → 主应用插件(读取操作)

读取方读取数据用途说明
主应用插件​所有整合后的配置数据基于完整状态启动服务、合并配置、处理交互
  1. 构建插件 ↔ 存储系统(资源读写)

插件存储目标操作类型具体操作
应用资源插件​本地缓存写入下载依赖应用并缓存到node_modules
应用资源插件​主输出目录写入搬运资源到UNI_OUTPUT_DIR
模块暴露插件​远程资源读取从CDN读取根应用的manifest.json
模块暴露插件​本地缓存读取兜底读取本地缓存的配置
清单管理插件​发布目录写入写入Manifest文件和拷贝产物到PUBLISH_PATH
  1. 主应用插件 ↔ 运行时服务(服务启动)

服务启动方式交互说明
WebSocket Server​主应用插件启动建立实时通信通道,支持热更新
HTTP Server​主应用插件启动提供静态资源服务,支持子应用访问
配置合并服务​主应用插件触发合并所有子应用的pages.json生成最终的app.json
  1. 主应用插件 ↔ 子应用(双向交互)

交互方向交互组件交互内容通信方式
主应用 → 子应用​主应用插件 → WS客户端建立连接、监听变更、初始化同步WebSocket
子应用 → 主应用​文件监听器 → 主应用插件文件变更上报、增量更新通知事件上报
  1. 运行时服务 ↔ 子应用(运行时通信)

服务子应用组件通信内容通信协议
WebSocket Server​WebSocket客户端双向实时通信、状态同步、热更新推送WebSocket
HTTP Server​Vite构建静态资源访问、模块加载、API调用HTTP/HTTPS
  1. 子应用接入

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_ROOTboolean是否为主应用,主应用必须为 true
MFE_UNI_CODEstring小程序唯一标识
MFE_APP_CODEstring子应用唯一标识
MFE_CDN_HOSTstringCDN 服务地址

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 后续演进规划

  1. 近期优化

  • 全局变量隔离:实现 uni全局对象的多实例隔离,避免跨应用污染
  • 路由跳转劫持:统一管理子应用间的路由跳转,增强可控性
  • 依赖版本统一:确保三方包版本一致性,避免多版本冲突
  1. 中期扩展

  • H5 微 前端 接入:扩展架构支持 H5 应用集成
  • 构建性能优化:探索并行构建、增量编译等加速方案
  • 监控体系完善:集成构建监控、运行时性能分析
  1. 长期愿景

  • 低代码集成:支持可视化模块编排
  • Serverless 部署:实现子应用独立部署、动态更新
  • 跨端统一:扩展支持支付宝、抖音等小程序平台