Skip to content

第18章 设计模式与架构决策

"架构决策的本质不是选择最好的方案,而是在当前约束下选择最合理的取舍。微前端十年,教会我们的不是某个框架有多好,而是在隔离与共享之间,永远没有完美的平衡点——只有适合此时此刻的平衡点。"

本章要点

  • 识别微前端生态中反复出现的 10 个核心设计模式,理解它们解决的本质问题
  • 深入剖析"隔离 vs 共享"这一微前端永恒张力的技术根源与工程权衡
  • 从失败案例和被放弃的方案中提炼出比成功经验更有价值的教训
  • 展望微前端下一个五年:Web Components 标准化、Server Islands、Edge Rendering
  • 理解微前端的终极形态不是"前端的微服务",而是"模块的联邦"
  • 作为全书收官章,建立从细节到全景的架构思维——不仅知道"怎么做",更知道"为什么这样做"

写完前面十七章,我们已经从 single-spa 的路由劫持、乾坤的 JS/CSS 沙箱、import-html-entry 的资源解析、Module Federation 的编译时共享、Wujie 的 iframe 增强、Web Components 的原生隔离等各个维度,完成了对微前端架构的全面解剖。如果把前面的章节比作"显微镜"——逐行逐函数地观察每个框架的每一处实现,那么本章我们需要换一种工具:望远镜

站在足够远的距离回望整个微前端生态,你会发现一个令人惊叹的事实:在那些看似各自为营的框架实现之下,存在着一组反复出现的设计模式。single-spa 在用,乾坤在用,Module Federation 在用,Wujie 也在用——只是表现形式不同。这些模式不是教科书上的学术练习,而是无数工程师在真实生产环境中,面对真实的隔离需求、真实的性能约束、真实的团队协作压力做出的真实选择。

同时,微前端十年的发展史本身就是一部"架构决策史"。每一次技术迭代——从 iframe 到路由分发,从运行时沙箱到编译时联邦——都意味着一次架构哲学的重新审视。那些被放弃的方案和失败的尝试,往往比最终胜出的方案包含更多的智慧。

本章是全书的收官之章。我们将从设计模式、架构张力、失败教训、未来展望四个维度,为你构建一幅微前端架构决策的全景图。

下图展示了微前端生态中 10 个核心设计模式按生命周期阶段的分布:

Facade 模式的关键价值不仅在于简化——更在于解耦。子应用的开发者不需要知道主应用用的是乾坤还是 single-spa,只需要暴露约定的生命周期函数。反过来,主应用也不需要知道子应用用的是 React 还是 Vue。Facade 在两者之间划出了一条清晰的契约边界。

深度洞察:Facade 模式在微前端中的一个微妙风险是"过度封装"。乾坤的 start() 函数隐藏了大量底层决策(沙箱类型、CSS 隔离策略、预加载策略),当这些默认决策不适合你的场景时,你需要"穿透" Facade 去修改行为。这就是为什么乾坤后来添加了越来越多的配置项——Facade 的简洁性与灵活性之间,存在天然的张力。

18.1.2 Proxy 模式:JS 沙箱的核心机制

Proxy(代理)模式为另一个对象提供一个替身或占位符,以控制对这个对象的访问。乾坤的 ProxySandbox 是微前端领域最经典的 Proxy 模式应用——它用 ES6 Proxy 拦截子应用对 window 的所有操作,在不修改真实 window 的前提下为每个子应用提供独立的全局环境。

typescript
// 乾坤 ProxySandbox 的核心实现(简化)
class ProxySandbox {
  private updatedValueSet = new Set<PropertyKey>();
  private fakeWindow: Record<PropertyKey, any>;

  proxy: WindowProxy;

  constructor(name: string) {
    const rawWindow = window;
    const fakeWindow = Object.create(null);
    this.fakeWindow = fakeWindow;

    this.proxy = new Proxy(fakeWindow, {
      get(target, prop) {
        // 某些属性必须从真实 window 读取
        if (prop === 'window' || prop === 'self' || prop === 'globalThis') {
          return proxy;  // 返回代理本身,形成闭环
        }

        // 优先从 fakeWindow 读取(子应用的修改)
        if (target.hasOwnProperty(prop)) {
          return target[prop];
        }

        // 兜底到真实 window(共享的全局 API)
        const value = rawWindow[prop as any];
        // 如果是函数,需要绑定正确的 this
        if (typeof value === 'function' && !isBoundFunction(value)) {
          return value.bind(rawWindow);
        }
        return value;
      },

      set(target, prop, value) {
        // 所有写操作都发生在 fakeWindow 上
        target[prop] = value;
        updatedValueSet.add(prop);
        return true;
      },

      has(target, prop) {
        return prop in target || prop in rawWindow;
      },
    });
  }
}

这段代码体现了 Proxy 模式的精髓:子应用以为自己在操作 window,实际上操作的是一个代理对象。所有读操作先查代理、再查真实对象;所有写操作只发生在代理上。这实现了"读时共享、写时隔离"的效果——本质上是 Copy-on-Write 策略在 JS 全局对象上的应用。

18.1.3 Strategy 模式:可切换的隔离策略

下图展示了 Strategy 模式在微前端隔离策略中的应用,运行时根据配置选择不同的隔离算法:

基于 VitePress 构建