Skip to content

前端架构设计与微前端系统学习文档 ​

适合人群:已经能独立完成前端项目,希望掌握大型前端应用架构、模块边界、状态与数据流、设计系统、微前端和长期演进治理的学习者。

学习目标:从单应用分层开始,逐步掌握大型应用模块化、API 契约、状态治理、组件库与设计系统、微前端拆分、架构决策和迁移策略。

核对时间:2026-06-30。本文参考 React、Vue、Next.js、single-spa、Module Federation、Storybook、OpenAPI、GraphQL、W3C Design Tokens 等官方资料整理。


目录 ​

  1. 前端架构设计解决什么问题
  2. 从单页面应用到大型前端系统
  3. 应用分层与目录设计
  4. 模块边界与依赖治理
  5. 路由架构与权限模型
  6. 状态管理架构
  7. 数据请求与 API 契约
  8. 组件架构与设计系统
  9. 渲染架构:CSR、SSR、SSG、Edge
  10. 微前端架构
  11. 插件化与低代码架构
  12. 前端 BFF 与 Node.js 中间层
  13. 架构决策、迁移与技术债治理
  14. 架构师常见反模式
  15. 学习路线与练习
  16. 官方参考资料

1. 前端架构设计解决什么问题 ​

前端架构设计的目标不是把目录分得更漂亮,而是在业务复杂、团队变大、需求长期变化时,让系统仍然能被理解、修改、测试、发布和治理。

它主要解决:

  • 代码放在哪里。
  • 模块之间如何依赖。
  • 页面如何组织。
  • 状态放在哪里。
  • 数据如何请求、缓存、失效。
  • 组件如何复用。
  • 业务逻辑如何沉淀。
  • 多团队如何协作。
  • 旧系统如何迁移。
  • 技术债如何可控。

架构设计的好坏,通常不是项目第一周能看出来,而是在一年后需求变化、团队扩张、功能交叉、线上事故、框架升级时显现。

1.1 好架构的特征 ​

好架构通常具备:

  • 边界清晰。
  • 依赖方向稳定。
  • 公共能力可复用。
  • 业务模块低耦合。
  • 变更影响范围可预期。
  • 测试容易写。
  • 构建和发布可自动化。
  • 性能和安全有治理入口。
  • 新人能较快理解。

1.2 差架构的表现 ​

常见表现:

  • 页面直接调用底层请求工具。
  • 业务逻辑散落在组件里。
  • 全局 store 什么都放。
  • 公共组件依赖业务接口。
  • 任意模块都能互相 import。
  • 修改一个功能影响多个页面。
  • 组件库版本升级不敢动。
  • 没有统一错误处理。
  • 没有测试和可观测性。
  • 技术债没人记录。

2. 从单页面应用到大型前端系统 ​

2.1 小项目 ​

小项目可以简单:

text
src/
  components/
  pages/
  utils/
  api/
  App.tsx

这类结构适合:

  • 页面少。
  • 团队小。
  • 业务简单。
  • 生命周期短。

2.2 中型项目 ​

中型项目开始需要按业务组织:

text
src/
  app/
  pages/
  features/
  shared/

目标是避免 components、utils、api 变成垃圾桶目录。

2.3 大型项目 ​

大型项目要关注:

  • 多业务域。
  • 多团队协作。
  • 组件库。
  • 权限体系。
  • 复杂状态。
  • API 契约。
  • 构建性能。
  • 发布节奏。
  • 监控与回滚。

可能演进为:

text
apps/
  admin/
  portal/
packages/
  ui/
  hooks/
  api-client/
  config-eslint/
  config-ts/
  design-tokens/

这时架构设计已经和工程化、Monorepo、发布治理紧密相关。


3. 应用分层与目录设计 ​

3.1 分层原则 ​

目录不是重点,依赖方向才是重点。

推荐原则:

  • 上层依赖下层。
  • 下层不依赖上层。
  • 业务模块通过 public API 暴露。
  • 公共层不包含业务知识。
  • 页面层负责组装,不承担过多业务逻辑。

3.2 示例结构 ​

text
src/
  app/
    providers/
    router/
    styles/
    main.tsx
  pages/
    dashboard/
    user-list/
  features/
    user-management/
      api/
      model/
      ui/
      index.ts
    order-management/
      api/
      model/
      ui/
      index.ts
  entities/
    user/
    order/
  shared/
    api/
    config/
    lib/
    ui/
    types/

3.3 各层职责 ​

app:

  • 应用入口。
  • 全局 provider。
  • 路由初始化。
  • 全局样式。
  • 错误边界。

pages:

  • 页面级组件。
  • 路由参数读取。
  • 页面布局。
  • 组装 feature。

features:

  • 具体业务能力。
  • 业务 UI。
  • 业务状态。
  • 业务 API。

entities:

  • 领域实体。
  • 实体类型。
  • 实体展示组件。
  • 实体基础逻辑。

shared:

  • 通用 UI。
  • 通用 hooks。
  • 请求基础设施。
  • 工具函数。
  • 配置。

3.4 什么时候不需要复杂分层 ​

不要为了“看起来架构化”强行分层。

不需要复杂分层的情况:

  • 项目很小。
  • 生命周期很短。
  • 只有一两个开发者。
  • 页面之间几乎没有复用。
  • 业务仍在快速试错。

架构的目标是降低复杂度,不是制造仪式感。


4. 模块边界与依赖治理 ​

4.1 为什么模块边界重要 ​

没有边界的项目会逐渐变成:

text
任意页面 -> 任意组件 -> 任意 store -> 任意 api -> 任意 util

短期开发很快,长期会导致:

  • 代码难删。
  • 复用失控。
  • 模块不能独立测试。
  • 无法拆包。
  • 无法迁移。
  • 无法并行开发。

4.2 Public API ​

每个业务模块只通过 index.ts 暴露公共能力:

text
features/user-management/
  api/
  model/
  ui/
  index.ts

其他模块只能:

ts
import { UserTable } from "@/features/user-management";

不允许:

ts
import { UserTable } from "@/features/user-management/ui/UserTable";

好处:

  • 内部结构可调整。
  • 暴露面可控。
  • 依赖关系清晰。

4.3 用工具约束边界 ​

人工约定很容易失效。建议使用:

  • ESLint no-restricted-imports。
  • 自定义 ESLint 规则。
  • Nx module boundaries。
  • TypeScript path aliases。
  • 包级边界。
  • CI 检查。

示例:

js
rules: {
  "no-restricted-imports": [
    "error",
    {
      patterns: [
        "@/features/*/*",
        "!@/features/*/index"
      ]
    }
  ]
}

4.4 依赖方向 ​

推荐:

text
app -> pages -> features -> entities -> shared

禁止:

text
shared -> features
entities -> pages
features/order -> features/user/internal

如果两个模块互相依赖,通常说明:

  • 领域边界不清。
  • 有公共逻辑应抽到 shared 或 entities。
  • 需要通过事件、回调或 API 解耦。

5. 路由架构与权限模型 ​

5.1 路由不是简单页面列表 ​

大型应用路由还包含:

  • 权限。
  • 菜单。
  • 面包屑。
  • 标签页。
  • 页面缓存。
  • 数据预加载。
  • 错误边界。
  • 懒加载 chunk。
  • 埋点。

5.2 路由配置示例 ​

ts
export const routes = [
  {
    path: "/users",
    title: "用户管理",
    requiredPermissions: ["user:read"],
    lazy: () => import("@/pages/user-list"),
  },
  {
    path: "/orders",
    title: "订单管理",
    requiredPermissions: ["order:read"],
    lazy: () => import("@/pages/order-list"),
  },
];

5.3 权限模型 ​

常见模型:

  • RBAC:基于角色。
  • ABAC:基于属性。
  • ACL:访问控制列表。
  • Feature Flag:功能开关。

前端权限只能改善体验,不能替代服务端权限校验。

前端负责:

  • 控制菜单显示。
  • 控制按钮显示。
  • 控制路由访问。
  • 降低误操作。

服务端负责:

  • 真实鉴权。
  • 数据权限。
  • 操作权限。
  • 审计。

5.4 权限治理 ​

建议:

  • 权限码集中定义。
  • 路由权限和按钮权限统一来源。
  • 复杂权限封装成 hook / directive。
  • 权限失败统一降级。
  • 不在页面到处写散乱字符串。

6. 状态管理架构 ​

6.1 状态分类 ​

状态先分类,再选择工具。

状态类型示例推荐位置
UI 状态弹窗开关、tab、hover组件局部
表单状态输入值、校验、dirty表单库或组件局部
服务端状态列表、详情、缓存TanStack Query / Apollo
全局业务状态用户、租户、主题全局 store
URL 状态搜索条件、分页query string
派生状态过滤结果、统计值computed / selector

6.2 全局 store 不应承载一切 ​

错误做法:

  • 所有接口数据放 Redux / Pinia。
  • 所有弹窗状态放全局。
  • 表单输入实时同步全局。
  • 页面临时状态全局化。

后果:

  • 更新范围过大。
  • 调试困难。
  • 状态生命周期混乱。
  • 内存增长。
  • 页面之间意外影响。

6.3 服务端状态 ​

服务端状态特点:

  • 来源在服务端。
  • 可能过期。
  • 需要缓存。
  • 需要重新拉取。
  • 可能多个组件共享。
  • 有 loading / error / success 状态。

适合用 TanStack Query、SWR、Apollo Client 等工具。

6.4 状态管理选型 ​

选择时考虑:

  • 项目复杂度。
  • 团队熟悉度。
  • 框架生态。
  • 是否需要时间旅行调试。
  • 是否需要不可变数据。
  • 是否需要服务端状态缓存。
  • 是否支持 SSR。

不要因为某个库流行就引入。状态管理工具越多,心智负担越重。


7. 数据请求与 API 契约 ​

7.1 请求层职责 ​

统一请求层应负责:

  • baseURL。
  • headers。
  • token 注入。
  • token refresh。
  • 错误码处理。
  • 超时。
  • 请求取消。
  • 重试。
  • 幂等。
  • 日志和 trace id。
  • mock。

页面不应该直接散落:

ts
fetch("/api/users").then(...)

而应该通过统一客户端:

ts
const users = await userApi.listUsers(params);

7.2 API 契约 ​

常见契约方式:

  • OpenAPI。
  • GraphQL schema。
  • tRPC。
  • Protobuf。
  • 手写 TypeScript 类型。

推荐优先使用可生成类型的契约:

  • 后端改字段时能提前发现。
  • 前端减少手写类型错误。
  • mock 和文档更统一。

7.3 错误处理 ​

错误要分层:

  • 网络错误。
  • 超时。
  • 401 未登录。
  • 403 无权限。
  • 404 资源不存在。
  • 409 冲突。
  • 500 服务端错误。
  • 业务错误码。

统一策略:

  • 全局错误拦截。
  • 页面局部可覆盖。
  • 用户可理解的错误提示。
  • 可观测性记录上下文。
  • 防止重复弹窗。

7.4 缓存与失效 ​

缓存策略要明确:

  • 哪些数据可缓存。
  • 缓存多久。
  • 什么时候失效。
  • 用户切换租户是否清空。
  • 权限变化是否清空。
  • 更新后是否乐观更新。

前端缓存一旦设计不清,很容易出现“页面显示旧数据”的线上问题。


8. 组件架构与设计系统 ​

8.1 组件分层 ​

常见组件类型:

  • 基础组件:Button、Input、Modal。
  • 组合组件:SearchForm、DataTable。
  • 业务组件:UserSelector、OrderStatusTag。
  • 页面组件:UserListPage。

基础组件不应该依赖业务接口。

业务组件可以依赖业务模型,但要控制范围。

8.2 组件 API 设计 ​

好的组件 API:

  • 命名清晰。
  • 行为稳定。
  • 默认值合理。
  • 可组合。
  • 可扩展。
  • 可访问。
  • 类型明确。

示例:

tsx
<DataTable
  columns={columns}
  dataSource={users}
  rowKey="id"
  loading={loading}
  pagination={pagination}
  onChange={handleTableChange}
/>

8.3 受控与非受控 ​

受控组件:

tsx
<Input value={value} onChange={setValue} />

非受控组件:

tsx
<Input defaultValue="Ada" />

组件库要明确支持哪种模式。复杂表单组件通常要支持受控模式,方便表单库集成。

8.4 设计系统 ​

设计系统包含:

  • 设计原则。
  • Design Tokens。
  • 组件规范。
  • 交互规范。
  • 内容规范。
  • 可访问性规范。
  • 文档站。
  • 版本策略。

组件库只是设计系统的工程实现之一。

8.5 Design Tokens ​

示例:

json
{
  "color": {
    "primary": {
      "value": "#1677ff"
    }
  },
  "radius": {
    "sm": {
      "value": "4px"
    }
  }
}

Tokens 可以生成:

  • CSS variables。
  • Tailwind theme。
  • iOS / Android 变量。
  • Figma variables。

9. 渲染架构:CSR、SSR、SSG、Edge ​

9.1 CSR ​

CSR 适合:

  • 后台系统。
  • 强交互应用。
  • SEO 不重要的内部系统。
  • 用户登录后使用的产品。

风险:

  • 首屏依赖 JS。
  • 低端设备慢。
  • SEO 弱。

9.2 SSR ​

SSR 适合:

  • 内容站。
  • 电商详情页。
  • SEO 关键页面。
  • 需要较快首屏的页面。

风险:

  • 服务端成本。
  • hydration 成本。
  • 缓存复杂。
  • 部署复杂。

9.3 SSG ​

SSG 适合:

  • 文档。
  • 博客。
  • 营销页。
  • 内容稳定页面。

风险:

  • 动态内容不灵活。
  • 构建时间可能很长。

9.4 Edge Rendering ​

Edge 适合:

  • 全球用户。
  • 个性化程度适中。
  • 低延迟要求。
  • CDN 边缘可计算场景。

风险:

  • 运行时限制。
  • 调试复杂。
  • 成本和平台绑定。

9.5 选择方法 ​

先问:

  1. 是否需要 SEO?
  2. 首屏是否影响业务转化?
  3. 内容是否频繁变化?
  4. 用户是否全球分布?
  5. 页面交互是否很重?
  6. 团队是否有服务端部署能力?

不要为了追新把后台系统强行 SSR。


10. 微前端架构 ​

10.1 微前端是什么 ​

微前端是把大型前端应用拆成多个可独立开发、独立构建、独立发布的前端子应用,并在运行时或构建时集成。

它的目标是组织协作和系统演进,不是单纯技术炫技。

为什么拆分会带来复杂度 ​

"独立发布"的反面是"集成正确性由谁保证"。单体应用里,模块间靠同一份代码、同一次构建、同一个运行时保证一致性;拆开后,这三件事同时消失:

原本免费获得拆分后要显式设计
同一份依赖依赖共享与版本协商(MF / externals / 多实例代价)
同一次构建契约测试、主/子版本兼容矩阵
同一个运行时JS 沙箱、全局变量污染、定时器与事件清理
同一套样式表样式隔离与弹窗挂载容器
同一路由实例路由同步、刷新恢复、多应用激活
同一个发布流水线灰度、回滚、远程资源不可用降级
同一次错误上报故障归因到哪个子应用

架构师评估微前端时,评估的是这七项显式成本能否被"独立发布带来的组织收益"覆盖。

10.2 适用场景 ​

适合:

  • 多团队维护同一个大应用。
  • 每个业务域发布节奏不同。
  • 遗留系统需要渐进迁移。
  • 不同业务需要不同技术栈。
  • 主应用需要平台化集成子应用。

不适合:

  • 项目很小。
  • 团队很小。
  • 只是组件复用。
  • 没有独立发布诉求。
  • 子应用之间强共享状态。

10.3 常见方案 ​

Module Federation ​

特点:

  • Webpack 5 生态提出。
  • 支持运行时加载远程模块。
  • 可共享依赖。
  • 适合模块级集成。

内部机制(理解这些才能解释它的边界):

  • 每个参与方构建产出一个容器(container),核心是 remoteEntry.js,暴露 get(module) 与 init(shareScope) 两个方法。宿主首次 import("remoteApp/Button") 时:① 动态插入 <script> 拉取 remoteEntry;② 调 container.init(__webpack_share_scopes__.default) 完成共享作用域握手;③ 调 container.get("./Button") 拿到工厂函数并同步执行。模块"加载(异步)"与"求值(同步)"分离。
  • 共享依赖协商发生在运行时:shared 配置声明 version、requiredVersion、singleton、eager 等,容器初始化时按 semver 在 shareScope 里选版本;版本不满足会告警并各自加载一份。singleton: true 只保证"尽量单例",版本冲突时仍可能降级为多实例——React 双实例报错(Invalid hook call)就是这么来的。
  • shareScope 在容器 init 时封 seal:后注册的 remote 不能向已初始化的运行时补共享模块,动态 remote 需注意初始化顺序。
  • 它不提供应用级生命周期、沙箱与样式隔离——同一页面同一 JS 运行时内,全局变量、CSS 全部共享。所以严格说它是"依赖共享/模块联邦",不是完整微前端容器,常与 single-spa 类路由框架组合。

生态现状:Webpack 5 原生提供 ModuleFederationPlugin;@module-federation/enhanced(联邦 2.0)把它扩展为独立 runtime + manifest(mf-manifest.json)体系,支持 Rspack、Vite(@module-federation/vite)、Next.js,补上了多 shareScope、预取、per-shared runtime 等能力,是目前的活跃方向。

适合:

  • 共享组件。
  • 共享页面模块。
  • 主应用动态加载远程应用。

single-spa ​

特点:

  • 以应用生命周期为核心。
  • 支持多个框架共存。
  • 路由驱动子应用挂载卸载。

内部机制:

  • 子应用只需导出 bootstrap / mount / unmount(可选 unload)三个异步生命周期函数,主应用用 registerApplication({ name, activeWhen, app }) 注册;single-spa 监听 popstate / hashchange(并对 pushState / replaceState 打补丁),路由变化时卸载 activeWhen 不再匹配的已挂载应用,再加载并挂载新匹配的应用。
  • 框架内部以应用状态机托管流程,官方 getAppStatus 返回的状态包括:NOT_LOADED、LOADING_SOURCE_CODE、NOT_BOOTSTRAPPED、BOOTSTRAPPING、BOOTSTRAPPED、NOT_MOUNTED、MOUNTING、MOUNTED、UNMOUNTING、UNMOUNTED、UNLOADING、LOAD_ERROR、SKIP_BECAUSE_BROKEN;生命周期每步以 Promise 串联,任一环节 reject 会把应用标记为 SKIP_BECAUSE_BROKEN 而不是拖垮整页——这就是 qiankun 说"继承 single-spa 基本盘"的含义。
  • 它只管"何时挂载/卸载哪个应用",不管 JS 隔离和样式隔离;隔离、加载策略等由上层(qiankun 等)补齐。
  • 子应用加载失败时框架提供 addErrorHandler 等钩子做降级,这是"远程资源不可用兜底"的标准落点。

适合:

  • 多子应用集成。
  • 技术栈共存。
  • 遗留系统迁移。

iframe ​

特点:

  • 隔离性强。
  • 集成简单。
  • 通信和体验成本高。

适合:

  • 第三方系统。
  • 安全隔离要求高。
  • 低频页面。

Web Components ​

特点:

  • 标准化组件封装。
  • 跨框架复用。
  • 生态和开发体验需要评估。

适合:

  • 跨框架组件。
  • 设计系统部分能力。

qiankun ​

特点(原理层):

  • 构建在 single-spa 之上,补上了应用加载与隔离能力:HTML entry 加载、JS 沙箱、样式隔离。
  • 子应用以 HTML 为入口:框架 fetch 子应用 HTML,解析出 JS/CSS 并劫持执行,因此子应用需暴露 bootstrap / mount / unmount 生命周期。
  • JS 沙箱默认开启:experimentalStyleIsolation 用选择器前缀改写作用域(@keyframes、@font-face、@import、@page 不被改写,是已知限制);strictStyleIsolation 把子应用容器转为 Shadow DOM 做严格隔离,但弹层挂到 document.body 时会脱离样式作用域,需要调整挂载容器。
  • 沙箱实现分两代:快照沙箱(激活时 diff window、卸载时还原)只支持单实例;Proxy 沙箱(legacySandbox 单 window 代理 / proxySandbox 独立 fakeWindow)支持多实例并行。
  • 子应用需配置 publicPath 运行时改写(如 __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__)和 UMD 输出格式,接入改造成本是所有方案里偏高的一档。

适合:

  • 国内团队、Vue/React 混合存量系统的集成平台。
  • 需要严格 JS 沙箱与生命周期管控的多子应用门户。

⚠️ 版本现状:生产主流仍是 qiankun 2.x(v2 文档独立维护);3.x 在积极开发中,重写为流式 HTML-entry 加载、Proxy-membrane 沙箱与原生 ESM 沙箱执行,API 与 2.x 不完全兼容。选型时以官方仓库 Roadmap 为准,不要按 2.x 假设长期 API 稳定。

wujie(无界,腾讯) ​

特点(原理层):

  • Web Components + iframe 双沙箱:JS 运行在 iframe 内(天然获得独立的 window/document/history/location,原生隔离),UI 渲染在主应用下的自定义元素 wujie-app 的 Shadow DOM 内(原生 CSS 隔离)。
  • 关键连接点是把 iframe 内 document 的查询接口(querySelector、getElementById、head、body 等)代理到 Shadow DOM,使子应用框架"以为自己在操作本页 DOM",实际渲染进主应用容器;弹窗 document.body.appendChild 也因此天然适配。
  • iframe 的 src 指向主应用域名(同域),因此主、子应用可直接访问 window.parent 通信;同时提供 props 注入和去中心化 EventBus 三种方式。
  • 路由同步通过劫持 iframe 的 history.pushState / replaceState 将子应用 URL 同步到主应用 query,浏览器刷新、前进、后退都能作用到子应用。
  • 组件式接入(<WujieVue> / startApp),无需注册表;支持子应用保活(keep-alive)、多应用同时激活、预执行;对 Vite(ESM)子应用支持较好。

适合:

  • 对隔离严格性、子应用零改造、保活/多应用激活有强诉求的场景。

注意事项:Shadow DOM 会阻断全局样式(antd 等组件库的全局样式需处理)、pointer-events/层级/无障碍焦点等跨 Shadow 边界的行为需要评估。

micro-app(京东) ​

特点(原理层):

  • 借鉴 WebComponent 思想但自实现:用 CustomElement 结合自定义样式隔离(scopecss,选择器加属性前缀改写)实现"类 WebComponent"渲染,避开原生 Shadow DOM 在部分框架(如 React 全局事件代理)下的兼容问题;原生 shadowDOM 属性默认关闭,可按需开启。
  • JS 沙箱有两种官方模式,可自由切换:默认 with 沙箱(with(proxyWindow) 包住子应用代码执行,配合缓存优化降低频繁代理开销);遇到 with 沙箱跑不动的场景(如 Vite/ESM 子应用)可加 iframe 属性切到 iframe 沙箱(隐藏 iframe 提供原生级全局隔离)。另有 disable-sandbox 可整体关闭沙箱(会连带失去样式/元素/路由隔离,官方不建议)。
  • 子应用零改造接入:只需主应用写一个 <micro-app name="xx" url="xx"> 标签(子应用设置允许跨域即可),路由通过 virtual route / forward 等方式同步。
  • 官方同时维护 micro-zoo(子应用侧适配层,处理 react-router 5/6、vue-router 的路由同步)。

适合:

  • 希望接入成本最低、类 iframe 使用体验但不接受 iframe 隔离代价的 Vue/React 混合系统。

方案原理对比总表 ​

维度qiankun 2.xwujiemicro-appModule Federation原生 iframe
JS 隔离Proxy/快照沙箱(模拟 window)iframe 原生沙箱with + Proxy 沙箱无(同一运行环境)原生,最强
CSS 隔离实验性前缀改写 / Shadow DOM 可选Shadow DOM(原生)scopecss 选择器改写(可选开 shadowDOM)无,靠约定/CSS Modules原生,最强
集成粒度应用级应用级应用级(组件化标签)模块/组件级页面级
子应用改造成本中(生命周期+publicPath+UMD)低极低中(构建配置 shared/exposes)无
路由同步single-spa 劫持 history劫持 iframe history,天然同步virtual route/forward 多种策略自行处理需 postMessage 或 sync
保活/多实例手动内建内建 keep-alive不适用天然
Vite/ESM 子应用需插件适配支持切换 iframe 沙箱支持(with 沙箱对 ESM 受限)Rspack 原生支持(联邦 2.0)无要求
共享依赖手动 externals/windowiframe 同域可 window.parentwindow.microApp 等注入一等能力(shareScope 版本协商)无法共享

💡 选型直觉:按模块复用诉求看选 Module Federation(它本质是依赖共享机制,不是应用容器,可与其他方案叠加);按应用集成诉求看,隔离严格性与保活看 wujie,生命周期管控和生态成熟度看 qiankun,接入成本最低看 micro-app,安全边界压倒一切(不可信第三方页面)才回到 iframe。

10.4 微前端关键问题 ​

路由 ​

需要明确:

  • 主应用负责一级路由。
  • 子应用负责内部路由。
  • 刷新后能否正确恢复。
  • 404 如何处理。

样式隔离 ​

方案:

  • CSS Modules。
  • BEM。
  • Shadow DOM。
  • CSS-in-JS。
  • 命名空间。

不要只依赖团队自觉避免样式冲突。

各方案的隔离强度与代价:

方案隔离原理优点代价 / 陷阱
命名空间 / BEM约定类名前缀零运行时成本靠人守规矩,全局样式(body{}、*{}、element-ui 类)照样泄漏
CSS Modules构建期哈希类名局部作用域明确只管编译期,运行时全局选择器不受限;:global 会破窗
选择器前缀改写(qiankun experimental)运行时给子应用样式加 div[data-qiankun-xxx] 前缀不改变 DOM 结构,弹窗可渲染到 body 外@keyframes/@font-face/@import/@page 不被改写;动态注入样式需拦截
Shadow DOM(qiankun strict / wujie)浏览器原生作用域真正物理隔离,内外都进不来/出不去全局样式(组件库 CSS、主题变量)不再穿透;弹层 portal 到 body 会丢样式;::part/CSS 变量是最常用的逃生通道

💡 无论哪种方案都要单独处理两件事:一是弹窗/下拉/Toast 的挂载容器(组件库默认挂 document.body,隔离方案下要么改挂到子应用容器内,要么样式跟不过去),二是主题与 CSS 变量(设计系统 tokens 需要显式注入到每个子应用作用域)。

依赖共享 ​

需要明确:

  • React / Vue 是否共享。
  • 版本不一致怎么办。
  • 公共组件库如何升级。
  • 子应用能否独立部署。

通信 ​

通信方式:

  • URL。
  • CustomEvent。
  • 事件总线。
  • 全局 store。
  • postMessage。
  • shared SDK。

建议优先使用低耦合通信。子应用之间直接互调会破坏独立性。

按耦合度和适用性排一个序:

方式耦合度适合不适合 / 风险
URL(path/query)最低可分享状态:页签、筛选、分页;刷新可恢复数据量大时 URL 爆长;敏感信息入 URL 会进历史记录与日志
props 注入(主→子单向)低登录态、主题、语言、权限等初始化上下文不适合高频变更数据(每次变更要重新传递)
事件总线 / CustomEvent / wujie EventBus中瞬时通知型:主题切换、用户信息刷新事件名散落无契约;忘 off 就是内存泄漏;调试链路断
全局 store(initGlobalState / window.microApp / Pinia 共享)中高主应用主导的多子应用共享状态订阅方对数据结构产生隐式依赖,字段变更波及多团队
postMessage低(跨域)iframe、真跨域集成需要自己定义消息协议 + origin 校验,否则是安全面
子应用直接 import 对方模块 / 互调方法最高基本没有形成隐式依赖网,破坏独立部署;用 MF 共享也要以"公共包"为边界

💡 无论选哪种,都建议把"通信契约"版本化:事件名/字段集中定义(如 shared 包导出 MicroAppEvents 常量与 TypeScript 类型),CI 可检查,避免字符串满天飞。

发布与回滚 ​

微前端最容易被低估的是发布治理:

  • 子应用版本。
  • 主应用兼容。
  • 灰度策略。
  • 远程资源不可用降级。
  • 回滚。
  • 监控归因。

10.5 微前端决策清单 ​

在采用前回答:

  1. 是否真的需要独立发布?
  2. 是否真的有多个团队?
  3. 子应用边界是否清晰?
  4. 共享依赖如何管理?
  5. 样式隔离怎么做?
  6. 权限和登录如何统一?
  7. 线上故障如何定位到子应用?
  8. 发布失败如何回滚?
  9. 性能预算如何分摊?
  10. 本地开发体验如何保障?

11. 插件化与低代码架构 ​

11.1 插件化适用场景 ​

适合:

  • 编辑器。
  • 可视化搭建平台。
  • 低代码平台。
  • 数据看板。
  • IDE 类产品。
  • 工作流平台。

插件化核心是扩展点设计。

11.2 扩展点 ​

常见扩展点:

  • 菜单。
  • 工具栏。
  • 面板。
  • 物料组件。
  • 数据源。
  • 动作。
  • 校验器。
  • 主题。
  • 渲染器。

11.3 插件系统难点 ​

  • 插件生命周期。
  • 权限控制。
  • 沙箱隔离。
  • 版本兼容。
  • 插件通信。
  • 性能。
  • 安全。
  • 调试。

11.4 Schema 驱动 ​

低代码常用 schema 描述页面:

json
{
  "type": "Form",
  "props": {
    "title": "用户信息"
  },
  "children": [
    {
      "type": "Input",
      "props": {
        "name": "username",
        "label": "用户名"
      }
    }
  ]
}

关键问题:

  • schema 版本。
  • schema 校验。
  • 组件映射。
  • 表达式安全。
  • 数据绑定。
  • 运行时性能。

12. 前端 BFF 与 Node.js 中间层 ​

12.1 BFF 是什么 ​

BFF:Backend For Frontend。它是为特定前端体验服务的后端层。

常见职责:

  • 聚合多个后端接口。
  • 适配前端数据结构。
  • 处理 SSR 数据。
  • 管理 session。
  • 做权限辅助。
  • 降低前端请求瀑布流。
  • 对接老系统。

12.2 什么时候需要 BFF ​

适合:

  • 页面需要聚合多个服务。
  • 后端 API 不适合前端直接使用。
  • SSR 需要服务端拿数据。
  • 多端需要不同数据视图。
  • 鉴权和 cookie 策略复杂。

不适合:

  • 简单 CRUD。
  • 团队没有 Node 运维能力。
  • 只是为了把请求转发一遍。

12.3 BFF 风险 ​

  • 前端团队承担服务端稳定性。
  • 权限边界不清。
  • 逻辑重复。
  • 部署复杂。
  • 性能瓶颈。

BFF 要有日志、监控、测试和回滚,不应只是“前端顺手写个 Node 服务”。


13. 架构决策、迁移与技术债治理 ​

13.1 ADR ​

ADR 示例结构:

md
# ADR-001 使用 pnpm workspace 管理 Monorepo

## 背景
多个前端项目需要共享组件库和工具配置。

## 备选方案
- 多仓库
- pnpm workspace
- Nx

## 决策
采用 pnpm workspace + Turborepo。

## 原因
- 团队学习成本低
- 构建缓存成熟
- 与当前包发布流程兼容

## 风险
- 需要治理依赖边界
- CI 需要调整缓存策略

13.2 大型迁移 ​

迁移原则:

  • 先评估收益。
  • 再做技术验证。
  • 再设计双轨方案。
  • 再分批落地。
  • 每批可回滚。

典型迁移:

  • Webpack 到 Vite。
  • Vue 2 到 Vue 3。
  • Babel 7 到 Babel 8。
  • JS 到 TS。
  • 多仓库到 Monorepo。
  • 自研组件库到设计系统。

13.3 技术债分级 ​

可以分为:

  • P0:线上风险,必须立即处理。
  • P1:阻塞核心业务演进。
  • P2:影响开发效率或维护成本。
  • P3:局部代码质量问题。

不是所有技术债都要马上还。架构师要判断债务利息和业务优先级。


14. 架构师常见反模式 ​

14.1 为了架构而架构 ​

表现:

  • 小项目上微前端。
  • 简单页面上复杂状态机。
  • 两个人团队上重型 Monorepo。
  • 没有复用需求就建组件平台。

原则:架构要解决真实问题。

14.2 工具替代设计 ​

用了 Nx 不等于有模块边界。

用了 Storybook 不等于有设计系统。

用了 Module Federation 不等于有微前端治理。

工具只提供能力,架构仍需要清晰规则。

14.3 过度抽象 ​

过度抽象会导致:

  • 新人难理解。
  • 修改链路变长。
  • 类型复杂。
  • 调试困难。
  • 实际复用率低。

抽象应来自重复和变化点,不应来自预感。

14.4 忽略发布和运维 ​

架构如果只考虑开发,不考虑发布、监控、回滚,就是半成品。

大型前端架构必须包含:

  • 构建。
  • 测试。
  • 发布。
  • 监控。
  • 回滚。
  • 事故处理。

15. 学习路线与练习 ​

15.1 基础阶段 ​

目标:理解分层和状态分类。

练习:

  1. 把一个小项目从 components/pages/utils 重构为 app/pages/features/shared。
  2. 画出模块依赖图。
  3. 区分 UI 状态、服务端状态和全局状态。
  4. 把请求层统一封装。

15.2 进阶阶段 ​

目标:能设计中型项目架构。

练习:

  1. 设计一个后台管理系统目录结构。
  2. 设计路由权限模型。
  3. 使用 TanStack Query 或同类工具管理服务端状态。
  4. 用 OpenAPI 或 GraphQL schema 生成类型。
  5. 用 ESLint 限制跨模块 import。

15.3 高级阶段 ​

目标:能治理多团队项目。

练习:

  1. 搭建 Monorepo。
  2. 抽离组件库。
  3. 建立 design tokens。
  4. 接入 Storybook 文档。
  5. 设计组件库版本发布策略。

15.4 架构师阶段 ​

目标:能做架构决策和迁移。

练习:

  1. 写一份微前端是否适用的决策文档。
  2. 设计 Module Federation 或 single-spa 方案。
  3. 设计旧项目到新架构的迁移计划。
  4. 写 ADR。
  5. 定义模块边界自动化检查规则。

16. 官方参考资料 ​

基于 VitePress 构建的个人学习文档知识库