可扩展的 React 组件架构

可扩展的 React 组件架构

复合组件、设计令牌与团队真正会采用的 tree-shakeable 组件库。

2026年4月18日12 分钟阅读作者 Shehzad Asadullah

当 React 应用从原型成长为生产系统时,组件架构决定了代码库是优雅扩展还是在自身复杂度下崩溃。复合组件、设计令牌和可摇树优化的库构成了现代 UI 系统工具包,用于构建灵活、高性能且可维护的界面。本指南为 2026 年交付生产 React 应用的团队探索每种模式及实用指导。

展示复合组件、设计令牌和模块化库结构的组件架构示意图
可扩展架构通过组合、令牌和模块化导入分离展示层关注点。

复合组件的理由

复合组件是一种模式:父组件管理共享状态和上下文,子组件处理特定渲染职责。用户通过嵌套命名子组件而非通过扁平 prop 接口配置一切来组合 UI。这种模式贯穿成熟设计系统 — 标签页、手风琴、菜单和模态框都受益于复合组合。

主要优势是 API 人体工程学。将单体 prop 驱动组件与复合替代方案对比:

// Prop-driven — becomes unwieldy as features grow
<Tabs
  items={[
    { label: "Overview", content: <Overview /> },
    { label: "Settings", content: <Settings />, disabled: true },
  ]}
  defaultIndex={0}
  onChange={handleTabChange}
  variant="underline"
/>

// Compound — flexible, readable, extensible
<Tabs defaultValue="overview" onValueChange={handleTabChange}>
  <Tabs.List>
    <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
    <Tabs.Trigger value="settings" disabled>Settings</Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="overview">
    <Overview />
  </Tabs.Content>
  <Tabs.Content value="settings">
    <Settings />
  </Tabs.Content>
</Tabs>

复合组件通过 React Context 通信。父组件创建带有共享状态 — 活动标签、打开状态、选中值 — 的 context provider,子组件消费该 context 渲染其 UI 部分。这种解耦意味着你可以添加新子组件而无需修改父 API。

实现复合组件

首先定义带类型化接口的 context。根组件用 provider 包裹子组件并接受配置 props。子组件从 context 读取并根据共享状态条件渲染。

  • 将子组件作为静态属性导出到根组件 — Tabs.ListTabs.Trigger
  • 在开发环境中验证组合,当子组件在父组件外使用时提供有用错误信息。
  • 用 useMemo 保持 context 值稳定,防止所有子组件不必要重渲染。
  • 同时支持受控和非受控模式以获得最大灵活性。

设计令牌作为单一事实来源

设计令牌是存储视觉设计决策 — 颜色、间距、排版、阴影、圆角 — 的命名实体,以平台无关的值表示。不要在组件中硬编码 #3b82f616px,而是引用语义令牌如 color.action.primaryspacing.component.md

这种间接在大规模时带来切实收益:

  • 一致性 — 每个组件引用相同令牌值,消除视觉漂移。
  • 主题化 — 交换令牌值以在浅色、深色和高对比度主题间切换。
  • 跨平台对齐 — 从单一来源导出令牌到 CSS、JavaScript、iOS 和 Android。
  • 设计开发协作 — 设计师和开发者共享共同词汇。
// tokens.ts — semantic design tokens
export const tokens = {
  color: {
    surface: {
      primary: "var(--color-surface-primary)",
      secondary: "var(--color-surface-secondary)",
    },
    text: {
      primary: "var(--color-text-primary)",
      muted: "var(--color-text-muted)",
    },
    action: {
      primary: "var(--color-action-primary)",
      primaryHover: "var(--color-action-primary-hover)",
    },
  },
  spacing: {
    xs: "0.25rem",
    sm: "0.5rem",
    md: "1rem",
    lg: "1.5rem",
    xl: "2rem",
  },
  radius: {
    sm: "0.25rem",
    md: "0.5rem",
    lg: "0.75rem",
    full: "9999px",
  },
} as const;

在全局样式表中定义 CSS 自定义属性,在组件中通过令牌对象引用。当设计团队更新品牌调色板时,你在一处更改值,每个组件自动更新。

摇树优化与模块化库

包体积直接影响应用性能,尤其在移动网络上。摇树优化在构建过程中消除死代码,但仅当你的库架构支持时才有效。重新导出每个组件的单体导出会迫使打包器包含整个库,即使你只使用一个按钮。

为最佳摇树优化构建组件库结构:

  • 从各自模块入口点导出每个组件。
  • package.json 中将包标记为无副作用。
  • 避免从单一 index 重新导出一切的 barrel 文件。
  • 对现代打包器使用 ESM 格式;仅在需要遗留支持时提供 CJS。
  • 保持 peer 依赖外部 — React、React DOM 和样式库不应被打包。
// package.json — enable tree-shaking
{
  "name": "@acme/ui",
  "sideEffects": false,
  "exports": {
    "./button": "./dist/button/index.js",
    "./input": "./dist/input/index.js",
    "./modal": "./dist/modal/index.js",
    "./tokens": "./dist/tokens/index.js"
  }
}

// Consumer imports only what they need
import { Button } from "@acme/ui/button";
import { tokens } from "@acme/ui/tokens";

用包分析工具测量影响。导入单个组件并验证生产包排除无关模块。若整个库出现在输出中,调查 barrel 导出和副作用声明。

文件夹结构与就近放置

组织结构影响开发者查找和修改组件的速度。就近放置原则 — 将相关文件放在一起 — 减少上下文切换,使组件成为可独立移动、测试和删除的自包含单元。

组件库的可扩展文件夹结构:

  • src/components/Button/Button.tsx — 组件实现。
  • src/components/Button/Button.test.tsx — 与组件就近的单元测试。
  • src/components/Button/Button.stories.tsx — Storybook 文档。
  • src/components/Button/index.ts — 公共导出面。
  • src/tokens/ — 共享设计令牌定义。
  • src/hooks/ — 跨多组件使用的共享 hooks。

避免按类型组织(每个特性的顶层 components/hooks/utils/)。构建应用级架构时按功能域分组,构建共享库时按组件分组。

组合优于配置

连接复合组件、设计令牌和可摇树优化模块的主线是哲学:偏好组合而非配置。组件应小而专注、可组合,而非带有数十个控制每种视觉变体的 props 的单体小部件。

应用开闭原则 — 组件对通过组合的扩展开放,对修改封闭。不要给按钮添加 showIcon prop,而是将图标作为子元素或插槽接受。不要提供五个枚举值的 size prop,而是暴露消费者可覆盖的 CSS 自定义属性。

这种方法产出经得起时间考验的组件库。新需求通过组合现有原语满足,而非修改久经考验的组件并 risking 整个系统的回归。

文档与采用

架构模式只有团队理解并一致采用时才交付价值。投资 Storybook 或类似文档平台,展示复合组件组合、令牌用法和导入模式。编写贡献指南,说明何时创建新组件 versus 扩展现有组件。

对重大结构选择运行架构决策记录 — 为何选择复合组件而非 render props,为何令牌放在 CSS 自定义属性而非 JavaScript 主题对象。未来团队成员将继承这些决策,需要上下文以保持一致性。

可扩展的 React 组件架构不是同时应用每种模式。而是为你的团队规模、应用复杂度和性能约束选择正确的抽象。复合组件处理灵活 UI 组合,设计令牌强制视觉一致性,可摇树优化模块保持包精简 — 三者共同构成支持从十个组件增长到一万个的基础。

喜欢这篇文章吗?

有项目或想法吗?我很乐意听你聊聊。

联系我