Writing
微前端与 SSR 共存:Layout Engine 的架构取舍
当一个跨境电商前台由多个团队并行维护,问题就不再是「要不要微前端」,而是「边界画在哪里」。
从组织问题开始
跨境电商前台有十几类页面:首页、类目、商品详情、营销落地页、购物车、结算。它们由不同团队维护,但共享商品、价格、币种、多语言这些基础能力。
单体架构下的典型症状是:一个团队要发版,所有人都得等;一个公共组件改动,要跑全量回归。于是平台决定引入 Layout Engine——按页面区域拆分模块联邦子应用,各自独立构建与发布。
边界:不是按技术分层,而是按发布单元
微前端最常见的错误是按「Header / Footer / 商品列表」切分,结果每个子应用都要依赖同一份全局状态,耦合反而更深。
这次切分的依据是发布节奏:
| 子应用 | 变更频率 | 归属 |
|---|---|---|
| 营销页 | 每周多次 | 增长团队 |
| 商品详情 | 每周 | 商品团队 |
| 结算流程 | 每月(谨慎) | 交易团队 |
| 壳层(路由、布局、通信) | 很少 | 平台团队 |
变更频率接近的模块放在一起,发布才不会互相牵制。壳层只做三件事:路由分发、事件转发、公共依赖注入。
通信:事件契约优先
子应用之间绝不直接 import,也不共享可变 store。所有跨模块交互通过结构化事件:
type ShellEvent =
| { type: 'cart:add'; skuId: string; quantity: number }
| { type: 'currency:change'; currency: string }
| { type: 'locale:change'; locale: string };
export function emit(event: ShellEvent): void {
window.dispatchEvent(new CustomEvent('shell:event', { detail: event }));
}
export function on(handler: (event: ShellEvent) => void): () => void {
const listener = (e: Event) => handler((e as CustomEvent<ShellEvent>).detail);
window.addEventListener('shell:event', listener);
return () => window.removeEventListener('shell:event', listener);
}
约定两条规则:
- 只增不改:事件字段只能追加,不能重命名或删除;破坏性变更走新事件名。
- 壳层兜底:币种、语言这类全局状态由壳层统一维护,子应用只发意图,不自己请求。
联调成本因此从「拉齐所有团队」降到「对齐一个 TypeScript 类型」。
SSR:按路由声明,而不是全局二选一
营销页需要 SEO 与首屏速度,必须 SSR;后台配置页需要交互与状态,CSR 更合适。Layout Engine 允许按路由声明渲染模式:
export const routeConfig = {
'/campaign/:slug': { render: 'ssr', template: 'campaign.hbs' },
'/account/orders': { render: 'csr' },
} as const;
SSR 页面由 Node 渲染引擎输出 HTML,模板使用 Handlebars 保证渲染性能与可维护性;客户端 hydration 后接管交互。CSR 页面直接走客户端路由。
难点在于数据获取要统一:如果 SSR 与 CSR 各自请求接口,就会出现首次渲染与客户端状态不一致(hydration mismatch)。我们的做法是把数据请求抽成同构层,SSR 时在服务端预取并注入初始状态,客户端复用同一份数据。
性能:SSR 不是银弹
实测发现,SSR 页面首屏变快了,但 TTFB 变差了。原因有两个:
- 渲染引擎串行请求多个接口,服务端等待时间叠加。
- 模板中包含了大量非关键模块。
改造措施:
- 接口并行化,非关键数据延迟到客户端加载。
- 首屏只渲染视口内模块,其余用占位 + 懒加载。
- 模板缓存与接口缓存分层,静态部分不进渲染流程。
最终首屏时间下降约 40%,TTFB 回到可接受范围。
多语言:编译期与运行时分工
- 静态文案在构建时按语言拆包,首屏只加载当前语言。
- 动态文案(商品标题、营销规则)走接口,运行时插值。
- 语言与币种切换通过事件广播,避免每个子应用重复请求。
结果与反思
- 子应用可独立发布,回归范围收敛到单个模块。
- SSR 营销页的 SEO 与首屏指标明显改善,CSR 页面交互不受影响。
- 事件契约让跨团队协作有了明确接口,而不是靠文档与口头约定。
微前端真正的收益是组织的解耦,代价是架构复杂度上升:多一份壳层、多一套通信约定、多一层构建配置。如果团队只有两三个人、发布节奏一致,这套架构只会增加负担。
判断标准很朴素:当「等别人发版」成为常态时,微前端才值得。