AI 设计系统的组件分级策略:基础、复合与业务组件的生成边界
一、AI 生成 Table 组件,生出了一个 800 行的巨物
让 AI 生成一个"支持排序、筛选、分页、行选择、列拖拽的数据表格",AI 尽职尽责地生成了一段 800 行的 JSX + 内联样式。代码能跑,但它是一个"不可维护的巨物"——排序逻辑和分页逻辑耦合在同一个 useEffect 里,列拖拽的 onDragEnd 回调中硬编码了 12 行 columnWidth 状态更新,筛选面板的 UI 和筛选逻辑的 Hook 写在一个文件里。任何一个需求的变更——比如"筛选面板改为下拉时显示而不是展开时显示"——都意味着在 800 行中大海捞针。
问题不在于 AI 代码质量。问题在于 AI 不知道组件的"分界线"。Table 应该是一个复合组件——它由 TableHeader(列头 + 排序)、TableRow(数据行 + 选择)、TablePagination(分页器)、TableFilter(筛选面板)四个子组件组合而成。这四个子组件中有三个(排序、选择、分页)是基础组件的组合,一个(筛选面板)是业务组件。AI 如果不知道这个分层,就会把它们全部扁平化地堆在一个 800 行的文件中。
组件分级策略是决定"AI 能生成什么、不应该生成什么"的护栏。没有分级的 AI 组件生成就像没有命名空间的 CSS——它生成的代码"能用",但放到整个设计系统中去,就像往一个精心摆放的书架里塞一本尺寸不匹配的书。
二、基础、复合、业务三层组件模型
flowchart TD
subgraph 第三层["业务组件 (AI 可生成)"]
C1["UserProfileCard<br/>用户信息卡片"]
C2["OrderTimeline<br/>订单时间线"]
C3["DashboardWidget<br/>数据仪表盘小组件"]
end
subgraph 第二层["复合组件 (AI 可辅助,人工验收)"]
B1["DataTable<br/>数据表格 = 排序+筛选+分页+选择"]
B2["FormBuilder<br/>表单构造器"]
B3["FileUploader<br/>文件上传器 = 拖拽区+进度条+预览"]
end
subgraph 第一层["基础组件 (人工维护,AI 不可生成)"]
A1["Button, Input, Select<br/>原子级 UI 元素"]
A2["Tooltip, Popover, Modal<br/>浮层容器"]
A3["Typography, Icon, Avatar<br/>内容展示"]
end
B1 –> A1
B2 –> A1
B3 –> A1
C1 –> B1
C3 –> B1
C2 –> B1
第一层:基础组件(Primitives)。 这些是设计系统的原子——Button、Input、Select、Checkbox、Radio、Switch、Tooltip、Modal、Typography、Icon。基础组件的特征:(1)纯展示或单值行为(不包含业务流程);(2)API 表面固定(Props 数量 < 15);(3)样式完全由 Design Token 驱动。基础组件不应由 AI 生成。 理由是投资回报率极低——一个 Button 组件手写只需要 60 行,但需要处理 5 种 variant × 4 种 size × 5 种 state × 2 种 icon 位置 = 200 种组合。AI 无法在所有组合中保持一致性,而一致性恰恰是基础组件的核心价值。
第二层:复合组件(Composites)。 由多个基础组件组合而成的功能单元——DataTable、FormBuilder、FileUploader、DateRangePicker。复合组件的特征:(1)内部状态管理比 UI 复杂;(2)多个子组件的交互时序有要求;(3)API 设计中包含回调函数和事件约定。复合组件可以由 AI 生成骨架,但必须经过人工验收。 AI 能处理好"把选择器放在表的左边、排序图标放在列头的右边"这种布局逻辑,但在"行选择状态和全选状态之间的联动"、"跨页多选的数据持久化"这些状态机逻辑上容易出错。
第三层:业务组件(Business Components)。 直接服务于业务场景的功能模块——UserProfileCard、OrderTimeline、DashboardWidget、ProductGallery。业务组件的特征:(1)高度依赖 API 返回的数据结构;(2)视觉形态可能在一周内变化 3 次;(3)不进入设计系统的组件库,只在业务仓库中复用。业务组件是 AI 生成的最佳发力点。 它们是一次性需求或低复用率需求的载体,手工编写的时间成本和 AI 生成的校对成本接近持平,而 AI 速度更快。
三、组件分级策略的工程实现
/**
* 组件分级策略引擎
*
* 三类边界:
* 1. 基础组件 → 人工维护(AI 不可生成)
* 2. 复合组件 → AI 生成骨架 + 人工验收状态机逻辑
* 3. 业务组件 → AI 生成全量 + 人工 Review
*/
interface ComponentClassification {
name: string;
tier: 'primitive' | 'composite' | 'business';
// 是否允许 AI 生成
aiGenerateEnabled: boolean;
// 需要人工验收的关键检查项
reviewChecklist: string[];
// 最大允许行数(超过时自动拆分为子组件)
maxLines: number;
}
const CLASSIFICATION_RULES: Record<string, ComponentClassification> = {
// 基础组件:人工维护,AI 禁入
Button: {
name: 'Button', tier: 'primitive', aiGenerateEnabled: false,
reviewChecklist: ['5 variant × 4 size 枚举完整性', '5 state 状态覆盖(hover/active/disabled/loading/focus-visible)', 'WCAG 对比度 ≥ 4.5:1'],
maxLines: 100,
},
Modal: {
name: 'Modal', tier: 'primitive', aiGenerateEnabled: false,
reviewChecklist: ['焦点陷获(Focus Trap)', 'Escape 键关闭', 'z-index 层叠管理', 'aria-modal="true"'],
maxLines: 150,
},
// 复合组件:AI 可生成骨架
DataTable: {
name: 'DataTable', tier: 'composite', aiGenerateEnabled: true,
reviewChecklist: [
'排序状态机(asc/desc/none 三态切换)',
'行选择 + 全选联动逻辑',
'跨页多选数据持久化',
'筛选条件变更 → 重置分页到第 1 页',
'空状态 vs 加载中 vs 错误状态的渲染分支',
],
maxLines: 300,
},
FormBuilder: {
name: 'FormBuilder', tier: 'composite', aiGenerateEnabled: true,
reviewChecklist: ['表单校验规则与 Schema 的映射', '异步校验的 loading 状态', '字段间联动(A 改变 → B 禁用)'],
maxLines: 250,
},
// 业务组件:AI 可全量生成
ProductCard: {
name: 'ProductCard', tier: 'business', aiGenerateEnabled: true,
reviewChecklist: ['API 数据结构与 Props 的对齐', '价格格式化(¥符号+千分位)', '图片加载失败 Fallback'],
maxLines: 150,
},
};
/**
* 代码行数检查器
* 如果 AI 生成的组件超过 maxLines,说明组件内部耦合了过多逻辑
* 自动提示应拆分为子组件
*/
function checkComponentSize(componentName: string, generatedCode: string): string[] {
const rule = CLASSIFICATION_RULES[componentName];
if (!rule) return [];
const warnings: string[] = [];
const lineCount = generatedCode.split('\\n').length;
if (lineCount > rule.maxLines) {
const exceeds = lineCount – rule.maxLines;
warnings.push(
`[${componentName}] 代码行数 ${lineCount} 超过上限 ${rule.maxLines}(超出 ${exceeds} 行)。` +
`请拆分为子组件。对于 tier="${rule.tier}" 的组件,超过 maxLines 通常意味着:` +
(rule.tier === 'composite'
? '状态管理逻辑应提取为自定义 Hook'
: '一部分 UI + 逻辑应拆分为独立的子组件')
);
}
return warnings;
}
/**
* 分级守卫:在 AI prompt 中注入组件分级约束
*/
function injectTierConstraint(
componentName: string,
tier: 'primitive' | 'composite' | 'business'
): string {
switch (tier) {
case 'primitive':
return `⚠️ ${componentName} 是基础组件,不应由 AI 生成。请参考 src/components/${componentName}/ 下的现有实现。`;
case 'composite':
return `📋 ${componentName} 是复合组件。AI 生成骨架后需人工验收以下状态机逻辑:\\n${
CLASSIFICATION_RULES[componentName]?.reviewChecklist.map((c) => ` – ${c}`).join('\\n')
}`;
case 'business':
return `✅ ${componentName} 是业务组件,AI 可生成完整代码。完成后需 Review Props 与 API 数据的对齐。`;
default:
return '';
}
}
export { CLASSIFICATION_RULES, checkComponentSize, injectTierConstraint, ComponentClassification };
四、组件分级的三个陷阱
陷阱一:把所有复合组件推到基础组件层。 团队用了一个"大而全"的 DatePicker,它内部有一个日历渲染器、一个快捷选择面板、一个时区转换器。团队把它标记为基础组件(因为它"应该是 UI 的基本构成单元")。结果 AI 被允许为这个 DatePicker 生成修改——在日历渲染逻辑中插入了一个 useMemo,导致切换月份时 useMemo 的依赖数组缺少 locale 变量,日期不随语言切换而更新。正确做法:DatePicker 应该被拆分为三个子组件——Calendar(基础)、QuickPanel(基础)、TimeZoneConverter(复合)。然后按各自的层级管理变更权限。
陷阱二:组件的"最大行数"硬编码。 上面代码中 maxLines 是硬编码的数字。但如果一个复合组件的复杂性不是由行数决定的呢?一个 200 行的组件如果包含一个 useReducer 管理 15 种 action,它的心智复杂度和 Bug 概率远高于一个 350 行的纯渲染组件。更好的指标是"状态变量数 × 条件分支数"——它反映了一个组件的状态空间规模。当状态空间 > 50 时,组件应该被拆分。
陷阱三:把业务组件默认为"可随时丢弃"。 "业务组件是低复用率的组件,AI 能生成就生成,出错了再改"——这种心态导致的问题在于,业务组件往往承担着"用户完成核心任务"的高频场景。一个"订单支付确认卡片"如果因为 AI 生成的代码在金额格式化时丢失了小数点后两位,用户直接损失的是金额。业务组件的 AI 生成必须搭配自动化测试(至少加载快照测试 + 关键数据流测试),不能"生成后裸奔上线"。

