组合模式三分法
基于微前端 2026 生态 · 核于 2026-07
速查
- 三分法按组合发生的时机与位置分:构建时组合(编译期打进同一产物)/ 服务端组合(请求时拼 HTML)/ 客户端运行时组合(浏览器里加载挂载)
- 构建时组合 = Fowler 点名的反模式:微前端发成 npm 包、容器当依赖装——「对产品任何一部分的变更,都必须重新编译发布每一个微前端」,锁步发布原样回归
- 服务端组合的经典形态:SSI/模板拼接——
<!--#include virtual="/fragment" -->在 Web 服务器层把各团队独立渲染的 HTML 片段插进页面 - Geers 同构配方:Custom Elements + SSI——首次渲染走服务端 include(快、韧性好),到浏览器后自定义元素接管交互,兼得首屏与动态性
- 2026 服务端形态升级为平台/边缘路由组合:Vercel Microfrontends 按 path 把请求路由到不同项目(
microfrontends.json声明),Cloudflare 系 Web Fragments 同路 - 客户端三径:iframe(隔离最好、集成最僵)/ JS 渲染函数(Fowler:「灵活 + 独立部署 → 我们的默认选择,野外最常见」)/ Web Components(自定义元素的 DOM 规范即团队间契约)
- qiankun 的 HTML Entry 是 JS 入口的工程化变体——子应用交出整个 HTML,框架解析并接管其中资源;细节归 qiankun 叶
- Module Federation 不是构建时组合:虽由打包器实现,但 remote 在运行时加载、各应用构建互相独立——归运行时组合
- 取舍主轴一:隔离强度与集成灵活度成反比(iframe 最隔离也最难融合);主轴二:首屏/SEO 与交互无缝成反比(服务端强首屏,客户端强无缝)
- 判别铁律:凡是要「重新构建整体」才能上线的组合,都退化为模块化单体——独立部署是微前端的及格线
一、先问:组合发生在哪、发生在何时
Fowler 长文列举的集成方式,按「什么时候、在哪里把碎片拼成整体」归成三类——技术会过时,这个分类轴二十年有效:
| 模式 | 组合时机 | 组合位置 | 独立部署 | 典型技术 |
|---|---|---|---|---|
| 构建时组合 | npm install + 编译 | CI 产物 | ✗ | npm 包集成、git submodule |
| 服务端组合 | HTTP 请求时 | Web 服务器 / 边缘 | ✓ | SSI/ESI、模板拼接、Vercel 平台路由 |
| 客户端运行时组合 | 页面运行时 | 浏览器 | ✓ | iframe、JS 渲染函数、Web Components、MF 运行时加载 |
先记住表里唯一的 ✗:三分法真正的分水岭不是「用什么技术」,而是独立部署这条及格线过不过得去。
二、构建时组合:反模式及其例外
做法很自然,所以坑过的团队最多:每个微前端发布成 npm 包,容器应用把它们列进依赖,构建时打成一个 bundle:
{
"dependencies": {
"@feed-me/browse": "^1.2.3",
"@feed-me/order-food": "^4.5.6",
"@feed-me/user-profile": "^7.8.9"
}
}好处看起来不少——产物单一、公共依赖能去重。但 Fowler 的批评一针见血:
我们必须重新编译并发布每一个微前端,才能发布产品任何一部分的变更。……这种锁步发布流程,正是微前端要消灭的东西。
对这种方式他「强烈建议不要采用」(would recommend strongly against)。你付出了微前端的全部治理成本(多仓库、版本协商、跨团队接口),却收不到头号收益(独立部署)——风险单元没有变小,发布队列没有消失,只是包管理器多了几行。
症状自查:「升级公共组件库要开 30 个 MR」「一次上线要按依赖顺序发 5 个包」「容器每周发版,只为消化子包 version bump」——命中即在此反模式中。
两个必要的辨析:
- 例外:如果你要的只是代码组织与复用(没有独立部署诉求),npm 包拆分 + monorepo 是正解——它叫模块化单体,是好架构,只是别按微前端的成本模型付费,见与相邻方案的关系。
- Module Federation 不在此列:MF 虽由打包器实现,但 remote 模块在运行时才被拉取,各应用构建互相独立、可独立发版——属于运行时组合。把 MF 误归为「构建时集成」是 2026 年还很常见的过时印象,细讲在 Module Federation 叶。
三、服务端组合:从 SSI 到边缘路由
3.1 模板 / SSI 拼接
最老也最耐用的一路:Web 服务器按 URL 组装页面,把各团队独立服务渲染的 HTML 片段插进模板。以 Nginx SSI 为例:
<html lang="en">
<body>
<h1>Feed me</h1>
<!--# include file="$PAGE.html" -->
</body>
</html>每个片段来自一个可独立部署的小服务。Geers 的示例更极致——每个团队的组件渲染结果直接可以 curl 到:curl http://127.0.0.1:3000/blue-buy?sku=t_porsche 返回一段 HTML,页面里写 <!--#include virtual="/blue-buy?sku=t_porsche" --> 即完成组合。异步数据导致片段变慢时,Geers 给的技巧是让片段先渲染骨架屏(skeleton),交互再由客户端补全。
3.2 Geers 同构配方:Custom Elements + SSI
纯客户端组合的软肋是首屏——「构建对外可访问的站点时,初始加载性能多半是要紧的」。Geers 的配方是两层叠加:首次渲染走服务端 include(快、SEO 友好、JS 挂了也有内容——正是五理念里的「韧性站点」),HTML 到浏览器后自定义元素 upgrade 接管交互。服务端负责第一屏,客户端负责后续动态——同一个组件、两条渲染通路。
工程后果:这是内容型/电商站点做微前端的默认答案;纯中后台(登录墙内、无 SEO 诉求)通常不值得为它维护两条渲染通路。
3.3 2026 形态:平台路由组合
服务端组合在 2026 的主流形态已经上移到 CDN/边缘层:Vercel Microfrontends 让多个独立项目(可各用各的框架)共享一个域名,请求按 path 路由到对应项目,microfrontends.json 一份配置声明路由关系,本地用官方 proxy 模拟整站。Cloudflare 系的 Web Fragments 走的也是「片段在边缘组合」的路子(见 2026 选型全景)。
本质:组合粒度 = 页面/路径时,把拼接职责整个交给平台,应用层几乎零侵入——没有沙箱、没有运行时容器。代价有二:跨微前端导航是整页跳转(平台再做导航优化),以及页面内部多团队并存做不到——页内组合仍要回到客户端方案。
四、客户端运行时组合:三条路径
4.1 iframe
「用 iframe 从独立子页面拼出一个页面很容易,它在样式与全局变量互不干扰上提供了不错的隔离度」——Fowler 对 iframe 的肯定实打实。批评同样实打实:路由、历史、深链接都更复杂,页面响应式布局更难做,跨应用集成的灵活性差。标签细节与安全属性见 HTML 章与浏览器安全章;2026 年 wujie 把 iframe 重新用作「纯 JS 沙箱」的新视角,在与相邻方案的关系展开。
4.2 JS 渲染函数(entry function)
Fowler 文中的默认方案:每个微前端用 <script> 引入,加载后在全局暴露一个渲染函数;容器决定哪个微前端该挂载,调用对应函数并传入容器节点:
// 每个微前端暴露自己的挂载入口
window.renderBrowse = (containerId) => { /* 把应用渲染进 #containerId */ };
// 容器按路由调用
window.renderBrowse('micro-frontend-root');Fowler 的评价:「这种方式的灵活性,加上独立部署能力,使它成为我们的默认选择,也是野外最常见的。」后续的框架化演进都长在这条路径上:single-spa 把「渲染函数」规范成 bootstrap/mount/unmount 生命周期协议;qiankun 的 HTML Entry 再进一步——子应用直接交出整个 HTML,框架解析出其中的 JS/CSS 并接管执行,接入成本更低(代价与机制见 qiankun 叶)。
4.3 Web Components
Geers 方案的骨架:每个团队用自选技术实现功能,包进一个自定义元素(如 <order-minicart></order-minicart>)。关键的一句:「这个元素的 DOM 规范(标签名、属性与事件)就是给其他团队的契约(contract)或者说公共 API。」团队前缀直接写进标签名(blue-buy、green-recos),所有权在 DOM 树里一目了然。Fowler 的补充定位:结果与 JS 渲染函数方式相近,「差别在于你选择按 web component 的方式行事」——把入口约定交给浏览器标准而不是自定义全局函数。micro-app 正是沿这条路做的组件化接入(见 micro-app 叶);WC 标准 API 本身不在本叶展开。
五、三分法取舍表
| 维度 | 构建时(npm) | SSI/模板 | 平台路由 | iframe | JS 渲染函数 | Web Components |
|---|---|---|---|---|---|---|
| 独立部署 | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 隔离强度 | 无 | 页面级天然 | 页面级天然 | 最强 | 靠沙箱补 | Shadow DOM 中等 |
| 首屏/SEO | 好 | 最好 | 好 | 差 | 一般 | 一般(SSR 需同构方案) |
| 页内多应用共存 | —— | ✓(片段级) | ✗(页面粒度) | ✓ 但笨重 | ✓ | ✓ |
| 深链/路由 | —— | 服务端管 | 平台管 | 难 | 需容器协调 | 需容器协调 |
| 典型代表 | 反模式案例 | 传统电商门户、Geers 配方 | Vercel MFE、Web Fragments | 传统门户、wujie 底层 | single-spa、qiankun | Geers 方案、micro-app |
三条选择建议收尾:
- 路径粒度能满足(不同团队管不同页面)→ 优先服务端/平台路由组合,复杂度最低,甚至不需要「微前端框架」;
- 页内多团队并存、跨栈混跑 → 客户端运行时组合,再在 iframe(强隔离)与 JS/WC(强融合)之间按信任度取舍;
- 无论选哪路,把「能否独立部署」当验收标准回测一遍——过不了,你搭的是模块化单体。
小结
组合模式三分:构建时组合因锁步发布回归被 Fowler 点名为反模式(例外是你本来就只要模块化单体);服务端组合从 SSI/模板一路演进到 2026 年的平台/边缘路由,吃下页面粒度的场景,首屏与 SEO 最强;客户端运行时组合覆盖页内共存与跨栈混跑,三条路径——iframe 重隔离、JS 渲染函数重灵活(Fowler 的默认选择,框架化为生命周期协议与 HTML Entry)、Web Components 把契约交给 DOM 标准。取舍围绕两根轴:隔离 vs 融合、首屏 vs 无缝。碎片拼起来之后,下一个问题是「URL 归谁管、容器该多厚」——路由分发与容器模式。