我的文档库

Monorepo UI 组件包设计:从设计系统到可维护组件库

该文档阐述 Monorepo UI 组件包设计,分为设计基础、原子、复合组件、UI 工具四层。遵循零业务耦合、主题化等原则,依托 Design Tokens 构建视觉体系,规范表单、浮层、图标实现,配套构建、Storybook 文档、测试,厘清通用 UI 与业务组件边界。

UI 组件包不是简单的组件集合,而是设计规范、主题系统、交互模式、组件 API 与文档工具链的组合。

1. UI 组件包的定位

一个成熟的 UI 组件包需要同时解决两个问题:

  1. 视觉一致性:不同应用使用相同的设计语言;
  2. 交互一致性:相同场景具有相近的行为、状态和反馈。

原方案将 UI 组件包定位为“设计系统 + 组件库 + UI 工具链”,而不是简单的组件堆积。fileciteturn1file1L43-L63

2. UI 组件包的四层结构

推荐从底到上划分为四层:

Design Foundation

Atomic Components

Composite Components

UI Utilities / Hooks
层级内容稳定性
设计基础层Design Tokens、主题、全局样式、响应式极高
原子组件层Button、Input、Image 等
通用复合层Select、Dialog、Toast、Tabs 等中高
UI 工具层useDialog、useToast 等

原方案采用相同的四层划分。fileciteturn1file1L52-L56

3. 核心设计原则

3.1 零业务耦合

组件库不应该直接调用业务 API,也不应该知道业务领域实体。

不推荐:

<UserPaymentButton userId={id} />

如果按钮内部直接发起支付请求,它已经不再是通用 UI 组件。

推荐:

<Button loading={loading} onClick={handlePay}>
  Pay
</Button>

业务行为由上层注入,组件负责视觉和交互。

3.2 可组合

组件应该像积木一样被组合,而不是把所有业务场景预先做成一个巨大组件。

3.3 主题化

颜色、字号、间距、圆角、阴影等视觉属性应该由 Design Tokens 控制。

3.4 类型完备

所有 Props、事件、Ref 和插槽都应该具有明确的 TypeScript 类型。

这些原则与原方案中的零业务耦合、可组合性、主题化驱动和类型完备保持一致。fileciteturn1file1L64-L70

4. Design Tokens:组件库的视觉内核

Design Tokens 是组件库长期维护的基础。

常见 Token 包括:

颜色

  • Brand;
  • Success;
  • Warning;
  • Error;
  • Neutral;
  • Text;
  • Background。

间距

例如:

4 / 8 / 12 / 16 / 24 / 32

字体

例如:

12 / 14 / 16 / 20 / 24

其他视觉参数

  • Border Radius;
  • Shadow;
  • Border;
  • Motion Duration;
  • Easing。

原方案建议将 Design Tokens 沉淀到共享 Tailwind Preset,再由组件库复用,避免多个包重复定义。fileciteturn1file1L43-L77

5. ThemeProvider 与 CSS Variables

推荐由主题 Provider 提供主题上下文:

<ThemeProvider theme="dark">
  <Button>Dark Theme</Button>
</ThemeProvider>

视觉 Token 最终可以映射为 CSS Variables:

:root {
  --color-primary: ...;
  --color-background: ...;
  --radius-md: ...;
}

这样可以支持:

  • Light / Dark;
  • 运行时切换;
  • 局部主题覆盖;
  • 多品牌主题。

6. 原子组件

原子组件数量不需要无限增加,但必须稳定。

Button

应该覆盖:

  • Primary / Secondary;
  • Loading;
  • Disabled;
  • Icon Button;
  • 点击反馈。

Input

应该覆盖:

  • Text;
  • Number;
  • Password;
  • Search;
  • Textarea;
  • Prefix / Suffix;
  • Error;
  • Disabled;
  • Controlled / Uncontrolled。

Image

除了显示图片,还应处理:

  • Loading;
  • Placeholder;
  • Error fallback;
  • Lazy loading;
  • srcset
  • object-fit
  • alt
  • CDN 参数。

原方案将 Button、Icon、Typography、Input、Image 作为高频原子组件,并强调状态一致性和可访问性。fileciteturn0file2L89-L108

7. 导航与流程组件

常见组件包括:

  • Breadcrumb;
  • Tabs;
  • Dropdown;
  • Pagination;
  • Steps;
  • Menu。

这些组件的重点不是“能显示出来”,而是统一页面流转、状态、键盘操作和响应式行为。

8. 表单体系

表单通常是企业级 UI 组件库中复用率最高的一部分。

推荐分层:

Input

Select / DatePicker / TimePicker

Form

Validation

Form 应该负责:

  • 布局;
  • 字段注册;
  • 取值与赋值;
  • 重置;
  • 提交;
  • 校验时机;
  • 字段联动。

业务校验规则不应该写死在组件库中。原方案明确要求 Form 只负责通用表单控制,业务规则由上层注入。fileciteturn0file2L124-L153

9. Feedback 与 Overlay

反馈组件负责统一操作结果和交互反馈:

  • Message;
  • Notification;
  • Tooltip;
  • Popover;
  • Popconfirm;
  • Modal;
  • Drawer。

这类组件需要统一处理:

  • z-index;
  • Portal;
  • ESC;
  • Focus;
  • 键盘操作;
  • Loading;
  • 动画;
  • 可访问性。

原方案特别强调浮层 z-index 的统一管理以及 ESC 关闭和无障碍支持。fileciteturn0file2L154-L169

10. UI Hooks

UI Hooks 与基础 Hooks 的边界应该明确。

适合组件包:

useModal
useToast
useDrag
useTextOverflow

适合基础工具包:

useDebounce
useThrottle
useInterval
useLocalStorage

判断标准可以概括为:

只要 Hook 的语义依赖 UI 行为,就应该靠近 UI 组件;如果只是纯逻辑能力,则应该放在基础 Hooks。

原方案也采用这一边界。fileciteturn0file2L170-L179

11. 图标体系

图标建议独立为 @org/icons

  • SVG 组件化;
  • 支持按需加载;
  • Tree Shaking;
  • 统一尺寸;
  • 单色 / 多色;
  • 自定义颜色。

这样可以避免图标数量增长后把整个组件包的构建和发布周期绑定在一起。原方案也建议图标库独立成包,并与组件库配套。fileciteturn0file2L180-L196

12. 构建策略

推荐:

  • ESM;
  • CJS;
  • DTS;
  • sourcemap;
  • sideEffects: false
  • 按组件导入。

例如:

{
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    }
  },
  "sideEffects": false
}

如果采用 Tailwind 体系,可以输出独立 CSS;如果采用 CSS-in-JS,则需要关注运行时开销和按需注入。原方案将两种样式策略都列为可选方案。fileciteturn0file2L205-L222

13. Storybook:让组件成为可观察资产

每个组件建议拥有对应 Story:

Button
├── Default
├── Loading
├── Disabled
├── WithIcon
└── LongText

Story 不只是 Demo,还可以成为:

  • API 使用示例;
  • 视觉回归入口;
  • 交互测试入口;
  • 设计评审入口;
  • 文档入口。

原方案将 Storybook 文档站作为组件库工程化的重要组成部分,每个组件对应 stories、API 说明和可交互 Playground。fileciteturn0file2L223-L227

14. 组件 API 设计

组件 API 建议遵循:

  1. Props 语义稳定;
  2. 受控与非受控行为明确;
  3. 默认行为可预测;
  4. 状态类型完整;
  5. Ref 行为明确;
  6. 不把业务字段写入通用组件;
  7. 避免过多 boolean Props。

例如:

<Button
  variant="primary"
  size="md"
  loading={loading}
  disabled={disabled}
  onClick={handleClick}
/>

而不是把多个业务行为塞进:

<Button isPayButton isVipButton isDepositButton />

15. 标准目录结构

packages/components/
├── src/
│   ├── index.ts
│   ├── theme/
│   ├── button/
│   ├── input/
│   ├── form/
│   ├── modal/
│   ├── hooks/
│   └── __tests__/
├── stories/
├── package.json
├── tsconfig.json
├── tsdown.config.ts
└── README.md

16. 版本策略

UI 组件的破坏性变更不仅可能影响 TypeScript API,还可能影响视觉和交互,因此应该同时关注:

  • Props 兼容性;
  • CSS class / Token;
  • DOM 结构;
  • 键盘行为;
  • 默认尺寸;
  • 默认间距;
  • 动画行为。

原方案特别指出 UI 组件升级影响包括视觉和交互,维护重点是 API 与视觉行为双向稳定。fileciteturn1file1L57-L63

17. 测试体系

建议至少包含:

  • 单元测试;
  • 交互测试;
  • Accessibility 测试;
  • Storybook 场景;
  • 视觉回归;
  • 构建产物测试。

对于 Modal、Dropdown、Tooltip、Form 等复杂组件,应重点测试焦点、键盘、Portal、异步状态和边界尺寸。

18. 组件何时应该进入业务域

一个非常重要的边界是:

通用 Button

通用 Dialog

业务 UserDialog

当组件开始出现以下特征时,就应该考虑上移到业务域包:

  • 包含业务字段;
  • 调用业务 API;
  • 包含领域状态;
  • 只能被某个业务域使用;
  • UI 行为与具体业务流程强绑定。

这样可以避免 UI 包最终变成“业务大杂烩”。

19. 总结

UI 组件库真正的核心资产不是组件数量,而是:

稳定的设计 Token、清晰的组件边界、可组合 API、统一的交互行为,以及完整的文档和测试体系。

组件越底层,越应该保持通用;业务越具体,越应该留在业务域包。

本页目录