结合常见组件库(如 Ant Design、Element UI)中的组件,用具体例子说明组合(含聚合)和继承的使用场景,更直观理解两者的区别:
一、组合(含聚合)的典型例子
组合是组件库中最核心的组织方式,以下是高频场景:
1. 布局类组件:职责分离的组合
-
Flex容器与子元素
Flex组件(父)通过flexDirection、justifyContent等属性控制整体布局,子元素(如div、Button、Card等)无需特殊定义,直接嵌套即可。- 逻辑:父组件负责布局规则,子组件仅需遵循父组件的布局约束,两者通过嵌套组合工作,子组件可独立存在(聚合关系)。
-
Grid栅格系统
Row(行)和Col(列)组件组合实现栅格布局:Row控制行间距,Col通过span属性控制占比,Col内部可嵌套任意组件(如Input、Image)。- 逻辑:
Row和Col是“整体-部分”的组合关系,Col依赖Row生效,但Col内的子组件可独立使用(聚合)。
- 逻辑:
2. 复杂结构组件:多子组件协同
-
Table表格
由Table(容器)、TableColumn(列定义)、TableHeader(表头)、TableRow(行)等子组件组合而成:Table负责数据管理、分页等整体逻辑;TableColumn定义每列的标题、数据字段、渲染方式(如插入Button作为操作按钮);- 子组件各司其职,通过 props 与父组件通信,无法单独脱离
Table工作(强组合关系)。
-
Form表单
Form(管理整体状态)+Form.Item(字段容器)+Input/Select等输入组件组合:Form.Item负责字段校验、错误提示,Input负责用户输入,两者通过Form的上下文关联;Input可独立用于非表单场景(聚合关系),Form.Item则依赖Form容器(强组合)。
3. 容器类组件:轻量聚合
-
Card卡片
由Card.Header(标题区)、Card.Body(内容区)、Card.Footer(操作区)组成,用户可在内容区插入Text、Image、Button等任意组件:- 子组件(如
Button)与Card是聚合关系,可脱离Card单独使用; Card仅提供容器样式,不干预子组件逻辑,通过组合实现灵活扩展。
- 子组件(如
-
Dialog弹窗
包含标题、内容区、底部按钮区,底部按钮通常用Button组件:Button是独立组件(可用于表单提交、列表操作等),在Dialog中仅作为“部分”存在(聚合)。
二、继承的典型例子(极少且受限)
继承在组件库中使用场景狭窄,通常用于“基础能力复用”或“同类型变种”:
1. 基础组件的统一抽象
组件库底层通常有一个 BaseComponent 父类,封装所有组件的通用能力:
- 如
className、style等样式属性的处理; - 事件监听(如
onClick)、ref引用传递等基础逻辑; - 所有组件(
Button、Input、Table等)都继承BaseComponent,避免重复实现通用逻辑。- 这里的继承是“is-a”关系:
Button是一种BaseComponent,复用其基础能力。
- 这里的继承是“is-a”关系:
2. 同类型组件的变种扩展
-
Button与LinkButton
LinkButton(链接样式按钮)继承Button的核心逻辑(点击事件、disabled状态等),仅修改样式(去除背景色、边框,模拟链接效果)。- 关系:
LinkButton是Button的一种特殊形态,符合“is-a”,且复用 90%以上逻辑,适合继承。
- 关系:
-
Input与SearchInput
SearchInput继承Input的输入逻辑,新增“搜索图标”和onSearch事件,本质是“带搜索功能的输入框”,属于Input的变种。
三、总结:从例子看优先级
- 组合无处不在:几乎所有复杂组件(
Table、Form、Card等)都通过组合实现,核心是“分拆职责,灵活组装”。 - 继承仅在底层或变种场景使用:如基础能力复用、同类型组件的微小扩展,且需严格满足“is-a”关系。
这也印证了“组合优先于继承”的原则——组合让组件更灵活、低耦合,而继承容易导致逻辑僵化,仅在极特殊场景下适用。