跳到主要内容
EN

微前端架构

10 分钟阅读

微前端核心理念

微前端借鉴了后端微服务的思想:将一个大型前端应用拆分为多个小型、独立、可自治的子应用,各自独立开发、部署、运行。

适用场景

  • 多团队协作的大型产品(如 ERP、SaaS 平台)
  • 技术栈渐进式迁移(旧 jQuery 逐步替换为 React)
  • 不同模块的发布节奏不一致

不适用的场景:小型项目、单一团队、技术栈统一——不要为了微前端而微前端。

graph TD
    A["主应用(壳)"] --> B["子应用 A<br/>React"]
    A --> C["子应用 B<br/>Vue"]
    A --> D["子应用 C<br/>Angular"]
    A --> E["公共依赖<br/>样式/工具库"]
    
    F["独立仓库"] --> B
    G["独立仓库"] --> C
    H["独立仓库"] --> D

集成方案对比

方案 隔离性 集成度 技术栈限制 性能 复杂度
iframe 完美隔离 差(额外进程)
Web Components 样式隔离
Module Federation 共享运行时
qiankun JS+样式隔离

iframe 方案

<iframe
  src="https://sub-app.example.com"
  style="width: 100%; height: 100%; border: none;"
></iframe>

优点:天然隔离,最简单。缺点:性能差(每个 iframe 独立进程)、URL 不同步、弹窗无法溢出、通信受限。

Web Components

class MicroApp extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: "open" });
    shadow.innerHTML = `
      <style>
        :host { display: block; } /* 样式隔离 */
      </style>
      <div id="app"></div>
    `;
    // 在 shadow DOM 中挂载子应用
    mountSubApp(shadow.querySelector("#app"));
  }
  disconnectedCallback() {
    unmountSubApp();
  }
}
customElements.define("micro-app", MicroApp);

优点:浏览器原生支持、Shadow DOM 样式隔离。缺点:JS 不隔离、生态不如主流框架、调试困难。

Module Federation

// 主应用 webpack.config.js
new ModuleFederationPlugin({
  name: "host",
  remotes: {
    dashboard: "dashboard@https://dashboard.example.com/remoteEntry.js",
    settings: "settings@https://settings.example.com/remoteEntry.js",
  },
  shared: {
    react: { singleton: true, requiredVersion: "^18" },
    "react-dom": { singleton: true, requiredVersion: "^18" },
  },
});

// 子应用 dashboard/webpack.config.js
new ModuleFederationPlugin({
  name: "dashboard",
  filename: "remoteEntry.js",
  exposes: {
    "./App": "./src/App",
  },
  shared: { react: { singleton: true }, "react-dom": { singleton: true } },
});
// 主应用中使用
const DashboardApp = React.lazy(() => import("dashboard/App"));

function App() {
  return (
    <Suspense fallback="加载中...">
      <DashboardApp />
    </Suspense>
  );
}

优点:运行时集成、共享依赖、真正按需加载。缺点:共享版本冲突风险、构建配置复杂。

qiankun

qiankun 基于 single-spa 封装,提供开箱即用的 JS 沙箱和样式隔离:

// 主应用
import { registerMicroApps, start } from "qiankun";

registerMicroApps([
  {
    name: "dashboard",
    entry: "//dashboard.example.com",
    container: "#subapp-container",
    activeRule: "/dashboard",
  },
  {
    name: "settings",
    entry: "//settings.example.com",
    container: "#subapp-container",
    activeRule: "/settings",
  },
]);

start({ prefetch: "all", sandbox: { strictStyleIsolation: true } });
// 子应用导出生命周期
export async function bootstrap() { /* 初始化 */ }
export async function mount(props) {
  ReactDOM.render(<App />, props.container.querySelector("#root"));
}
export async function unmount(props) {
  ReactDOM.unmountComponentAtNode(props.container.querySelector("#root"));
}

JS 沙箱隔离

qiankun 提供两种 JS 沙箱:

graph TD
    A["JS 沙箱"] --> B["Proxy 沙箱<br/>(多实例)"]
    A --> C["Snapshot 沙箱<br/>(单实例)"]
    
    B --> B1["每个子应用一个 fakeWindow"]
    B1 --> B2["Proxy 代理 window 访问"]
    B2 --> B3["子应用操作 fakeWindow"]
    B3 --> B4["不影响真实 window"]
    
    C --> C1["激活时快照 window"]
    C1 --> C2["卸载时恢复快照"]
    C2 --> C3["同一时刻只有一个子应用"]

Proxy 沙箱:为每个子应用创建 fakeWindow,通过 Proxy 拦截 window 的读写操作。多实例安全,性能略好。

Snapshot 沙箱:激活子应用时记录 window 快照,卸载时恢复。兼容性好(不支持 Proxy 的环境),但只能单实例运行。

样式隔离

策略 实现方式 效果
StrictStyleIsolation Shadow DOM 完全隔离,但弹窗等挂载到 body 的样式失效
ExperimentalStyleIsolation 运行时加 scoped 前缀 基本隔离,动态插入的样式可能泄漏
CSS Modules / Scoped CSS 编译时处理 推荐,框架层面隔离
CSS 命名约定 BEM 等 简单但依赖团队规范

推荐做法:主应用与子应用使用不同的 CSS 前缀,子应用内部使用 CSS Modules 或 Scoped CSS。Shadow DOM 方案在弹窗、下拉菜单等场景下有兼容性问题。

微前端治理

公共依赖管理

// externals 方案 — CDN 加载公共库
// webpack.config.js
module.exports = {
  externals: {
    react: "React",
    "react-dom": "ReactDOM",
  },
};

// HTML 中统一引入
<script src="https://cdn.example.com/react/18/react.production.min.js"></script>
<script src="https://cdn.example.com/react/18/react-dom.production.min.js"></script>

子应用通信

// 简单方案:CustomEvent
window.dispatchEvent(new CustomEvent("user-login", { detail: { userId: 1 } }));
window.addEventListener("user-login", (e) => console.log(e.detail));

// 中等方案:共享状态(initGlobalState)
import { initGlobalState } from "qiankun";
const { onGlobalStateChange, setGlobalState } = initGlobalState({
  user: null,
});
onGlobalStateChange((state) => { /* 全局状态变更 */ });
setGlobalState({ user: { name: "Alice" } });

// 复杂方案:微前端消息总线 + 本地存储

版本与部署治理

  • 每个子应用独立 CI/CD,独立版本号
  • 主应用配置子应用版本映射表,支持灰度发布
  • 子应用入口提供版本信息接口,主应用做兼容性检查
  • 回滚策略:子应用异常时自动降级到上一版本
编辑此页

评论