前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
AI 面试
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
开发者导航
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
AI 面试
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
开发者导航

微前端常见落地方案对比总结

首页2024-02-25 16:40:12Front-End
微前端qiankunModule FederationWeb Components前端架构

# 导语

在大型前端项目开发中,团队常常面临巨石应用的困境。随着业务迭代,项目代码量急剧膨胀,模块间耦合度高,开发效率低下,维护成本攀升。微前端作为一种将微服务理念拓展到前端开发的架构风格,能够将庞大的单体应用拆分为多个可独立开发、测试、部署的小型应用,从根本上解决团队协作和系统维护的难题。

本文将深入对比目前业界主流的五大微前端落地方案,包括基于 iframe 的传统方案、基于路由分发的 qiankun 与 single-spa、基于构建工具的 Module Federation、组件驱动的 Bit 方案,以及基于浏览器原生标准的 Web Components 方案。通过对各方案的技术原理、核心特性、优缺点和适用场景进行全方位剖析,并重点讲解 JS 沙箱机制 和 CSS 隔离方案 的实现原理,帮助技术团队快速建立对微前端的整体认知,并做出符合业务需求的技术选型决策。

# 什么是微前端

微前端(Micro Frontends)这一概念由 Thoughtworks 于 2016 年 11 月在 Technology Radar 文章中率先提出。其核心定义是:一种由独立交付的多个前端应用组成整体的架构风格,将前端应用分解成更小、更简单的能够独立开发、测试、部署的应用,而在用户看来仍然是内聚的单个产品。

Martin Fowler 给出了一个更为简洁的英文解释:An architectural style where independently deliverable frontend applications are composed into a greater whole. 微前端本质上借鉴了 2014 年提出的微服务(Microservices)架构思想,两者在解决的问题上高度相似——随着应用规模扩大,耦合度升高,导致缺乏灵活性,难以维护。

需要特别澄清的是,微前端并非一门具体的技术,而是一套整合了技术、策略和方法的完整架构体系。它可能以脚手架、配套工具和规范约束等多种形式呈现,不同方案各有利弊,适合业务场景的就是好方案。此外,微前端本身没有技术栈约束,技术栈无关不是微前端的固有要求,各团队可以根据实际情况选择 React、Vue 或其他框架。微前端也不要求各应用必须能独立运行,拆分的粒度可以是应用级、页面级,甚至组件级。

# 微前端的核心价值与适用场景

微前端的兴起主要源于三类业务需求。遗留系统迁移是最常见的原因——许多企业存在使用 Backbone.js、Angular.js 或 Vue.js 1 等老旧技术栈构建的应用,这些应用在线上稳定运行但缺乏新功能投入,在不重写原有系统的前提下接入新业务是极其诱人的选择。后端解耦、前端聚合是第二个重要需求,特别是 To B 应用场景,用户希望像使用单一产品一样使用企业提供的多个系统,聚合成为技术趋势。第三类需求是系统需要支持动态插拔机制,具备清晰的服务边界,支持不同团队独立演进。

然而,微前端并非万能解决方案。当团队具备系统内所有架构组件的话语权、有足够动力去治理和改造整个系统、或者系统本身已是不可分离的架构量子时,引入微前端可能带来不必要的复杂性。技术团队需要在引入微前端之前,充分评估收益与成本的平衡。

# 微前端五大主流方案对比

目前业界实现的微前端主流方案多达二十余种,按照技术实现方式可以划分为五大流派。接下来我们将逐一分析各流派的代表方案、技术原理和适用场景。

# 方案一:iframe 派

iframe 是最传统也是最简单的微前端实现方式。HTML 内联框架元素 <iframe> 表示嵌套的浏览上下文,能有效地将另一个 HTML 页面嵌入到当前页面中。这种方案的核心优势体现在三个方面:即来即用,开发者可以直接使用,无需引入额外依赖;隔离完美,iframe 可以创建全新独立的宿主环境,子应用之间互不干扰;组合灵活,支持在一个页面放置多个应用。

然而,iframe 方案存在多个致命缺陷。视窗大小不同步是典型问题,例如在 iframe 内的弹窗想要居中展示就非常困难。子应用间通信受限,只能通过 postMessage 传递序列化的消息,增加了开发复杂度。性能开销显著,加载速度慢、构建 iframe 环境会导致白屏时间过长。路由状态丢失也是常见痛点,刷新页面后 iframe 的 URL 状态会丢失,用户体验不佳。

无界(Wujie) 是腾讯在 2021 年推出的创新方案,定位为「基于 iframe 的全新微前端方案」。它继承 iframe 的优点同时补足其缺点,核心思路是:利用 iframe 的隔离性把 JS 代码放到 iframe 里执行(通过 Proxy),利用 Shadow DOM 的隔离性把子应用的 DOM 写到 ShadowRoot 中实现样式隔离。无界的主要优点包括单页面中多应用同时激活、隔离机制优雅、组件式使用方便;缺点是 Shadow DOM 和 Proxy 导致兼容性一般。

# 方案二:路由分发 + 资源处理派

这一流派是业界最主流的微前端实现方式,突出特点是 production-ready,提供完整的基座应用与子应用的主从关系、路由分发机制、子应用资源处理、JS 沙箱隔离、样式隔离支持以及完善的配套体系。代表方案包括基于 single-spa 的 qiankun、字节的 Garfish、阿里巴巴的 icestark 等。

# 2.1 single-spa 原理与实战

single-spa 是这一流派的开山鼻祖,核心定位是「为实现前端微服务化的 js 路由」。它实现了主应用通过路由匹配实现对子应用生命周期的管理。

核心原理:single-spa 通过监听浏览器 URL 的变化,在路由切换时判断是否需要加载或卸载某个子应用。核心思想是将整个应用视为一个 SPA,但在内部根据路由规则加载不同的子应用。每个子应用需要导出标准的生命周期方法供主应用调用。

single-spa 的核心 API 简洁明了:

// 主工程注册子应用
singleSpa.registerApplication({
    name: 'app1', // 子应用名称
    app: () => System.import('app1'), // 如何加载子应用(用户自定义)
    activeWhen: '/app1', // URL 匹配规则,表示何时激活这个子应用
});

// 启动主应用
singleSpa.start();
@前端进阶之旅: 代码已经复制到剪贴板

子应用需要导出三个核心生命周期方法:

// 子应用导出生命周期
import SubApp from './index.tsx';

export const bootstrap = () => {
    // 初始化逻辑,只在应用首次加载时执行一次
    console.log('子应用 bootstrap');
};

export const mount = (props) => {
    // 挂载逻辑,每次应用激活时执行
    ReactDOM.render(<SubApp />, props.container);
};

export const unmount = (props) => {
    // 卸载逻辑,每次应用切换时执行
    ReactDOM.unmountComponentAtNode(props.container);
};
@前端进阶之旅: 代码已经复制到剪贴板

使用 single-spa-vue 封装 Vue 子应用的示例:

// main.js
import singleSpaVue from 'single-spa-vue';

const appOptions = {
   el: '#vue',
   router,
   render: h => h(App)
}

// 在非子应用中正常挂载应用
if (!window.singleSpaNavigate) {
    delete appOptions.el;
    new Vue(appOptions).$mount('#app');
}

const vueLifeCycle = singleSpaVue({
   Vue,
   appOptions
});

// 子应用必须导出以下生命周期
export const bootstrap = vueLifeCycle.bootstrap;
export const mount = vueLifeCycle.mount;
export const unmount = vueLifeCycle.unmount;
export default vueLifeCycle;
@前端进阶之旅: 代码已经复制到剪贴板

single-spa 的优势与局限:

  • 优势:轻量级(小于 5kb gzip)、框架无关、路由劫持机制完善
  • 局限:不支持 JS 沙箱隔离、不支持 CSS 隔离,这也是众多框架基于它做二次封装的原因

# 2.2 qiankun 原理与实战

qiankun 是蚂蚁金服基于 single-spa 孵化的微前端实现库,定位为「快速、简单、完整的微前端解决方案」,口号是「可能是你见过最完善的微前端解决方案」。qiankun 是目前国内影响力最大的面向生产的微前端方案,也是唯一得到 single-spa 官方推荐的方案。

核心原理:qiankun 在 single-spa 基础上引入了 import-html-entry 库来加载和处理子应用的 HTML、JS、CSS 资源,并实现了完整的 JS 沙箱和 CSS 隔离机制。

主应用配置示例:

import { registerMicroApps, start } from 'qiankun';

// 注册子应用
const apps = [
  {
    name: 'vueApp', // 子应用名称
    // 默认会加载这个 HTML,解析里面的 js 动态执行(子应用必须支持跨域)
    entry: '//localhost:10000',
    container: '#vue', // 容器
    activeRule: '/vue', // 激活的路径
    props: { // 传递属性给子应用
      a: 1,
    }
  },
  {
    name: 'reactApp',
    entry: '//localhost:20000',
    container: '#react',
    activeRule: '/react'
  },
];

// 注册应用
registerMicroApps(apps);

// 启动
start({
  prefetch: false, // 取消预加载
  sandbox: true // 开启沙箱
});
@前端进阶之旅: 代码已经复制到剪贴板

子 Vue 应用配置:

// src/main.js
let instance = null;

function render(props) {
  instance = new Vue({
    router,
    render: h => h(App)
  }).$mount('#app');
}

// 独立运行微应用
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

// 如果被 qiankun 使用,动态注入路径
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

// 子应用的协议 - 导出供父应用调用
export async function bootstrap() {}
export async function mount(props) {
  render();
}
export async function unmount(props) {
  instance.$destroy();
}
@前端进阶之旅: 代码已经复制到剪贴板

子 React 应用配置:

# 2.3 Garfish 原理

# 方案三:Module Federation 派

配置示例:

# JS 沙箱机制详解

# 总结

fe
  • 导语
  • 什么是微前端
  • 微前端的核心价值与适用场景
  • 微前端五大主流方案对比
    • 方案一:iframe 派
    • 方案二:路由分发 + 资源处理派
      • 2.1 single-spa 原理与实战
      • 2.2 qiankun 原理与实战
      • 2.3 Garfish 原理
    • 方案三:Module Federation 派
    • 方案四:Component-driven 派
    • 方案五:Web Components 派
  • JS 沙箱机制详解
    • 快照沙箱
    • Proxy 代理沙箱
    • qiankun 的沙箱实现
  • CSS 隔离方案详解
    • 子应用之间样式隔离
    • 主应用与子应用之间的样式隔离
      • 方案一:BEM 命名规范
      • 方案二:CSS Modules
      • 方案三:Shadow DOM
      • 方案四:CSS-in-JS
      • qiankun 的样式隔离
  • 主流方案综合对比
  • 选型建议与最佳实践
  • 总结

← ESLint 9完全指南:新特性、重大变化与升级攻略React 18完全指南:新特性、并发模式与升级攻略 →