前端组件设计实践:从能复用到真好用,中间差了什么
前端项目里经常会强调组件化。
按钮做成组件,弹窗做成组件,表单项做成组件,最后看起来页面被拆得很细,代码也“似乎”更复用了。
但实际开发几年后,不少团队都会发现一个尴尬的问题:组件确实越来越多,可真正好用、敢复用、改起来不痛苦的组件却没有同步增加。
说到底,组件化不等于组件设计。
组件“能用”不代表“可维护”
很多组件第一次写出来时都很好用,因为它们只服务一个页面、一个场景、一个需求。
问题往往出在第二次、第三次复用时。
这时常见情况是:
- 新场景需要一个额外参数
- 旧交互要兼容新的样式
- 一个组件同时承担多个职责
- props 越加越多
组件没有坏,但它开始变重。
最常见的误区:为了复用而过度抽象
前端开发里一个很常见的问题是,过早抽象。
例如一个项目刚做了两个列表页,就急着提炼一个“超级列表组件”,希望分页、筛选、空态、加载态、表头、操作栏全部统一。
看起来很先进,实际上很容易把组件做成下面这种形态:
1<DataList 2 columns={columns} 3 showFilter={true} 4 filterSlot={...} 5 loading={loading} 6 emptyText="暂无数据" 7 showToolbar={true} 8 toolbarActions={...} 9 pagination={pagination} 10 rowActions={...} 11 customHeader={...} 12/>
参数越来越多,说到底是在用 props 模拟不同页面。这不是复用,而是在制造另一个配置系统。
好组件通常只解决一个明确问题
稳定的组件往往有一个共同特点:边界清楚。
例如:
- Button 只负责按钮语义和样式
- Modal 只负责弹层显示与关闭
- Table 只负责表格展示,不负责业务筛选逻辑
组件边界越清楚,后面就越容易判断:
- 这个需求该不该进组件
- 这个参数是不是越界了
- 这段逻辑应该放在外层还是内层
展示组件和业务组件最好分开
很多组件难复用,是因为它既负责 UI,又负责业务。
例如一个“用户列表组件”里同时做了:
- 请求接口
- 数据转换
- 空状态展示
- 表格渲染
- 点击跳转详情
这种组件在第一个页面里很高效,但一旦要给第二个页面复用,就会发现绑定得太死。
更稳的做法通常是把它拆成:
- 业务容器组件
- 纯展示组件
这样后者更容易复用,前者更容易调整。
props 太多,通常说明设计出了问题
一个组件的 props 增长到一定程度后,维护成本会明显上升。
因为这意味着:
- 使用者需要记住更多规则
- 参数组合变复杂
- 组件内部状态分支增多
这时比起继续加参数,更值得做的是回头判断:
- 组件职责是不是混了
- 是否应该拆成两个组件
- 是否应该把部分逻辑交给插槽或 children
组合往往比继承和配置更稳
前端组件里,最稳的复用方式通常不是一个万能组件,而是小组件组合。
例如一个卡片组件可以拆成:
CardCardHeaderCardContentCardFooter
这样每一部分都更明确,也更容易适配不同页面,而不是所有变化都靠布尔参数来开关。
样式统一要靠规范,不要只靠组件兜底
很多人希望通过组件库统一所有风格,这方向没错,但不能把所有规范都压在组件本身上。
如果团队没有基础设计规范,例如:
- 间距体系
- 字号层级
- 颜色语义
- 交互反馈规则
那组件只会变成“风格打补丁”的地方。
一个简单的判断标准
如果你在给组件加某个功能时,需要写一句“为了兼容另一个页面”,那就应该警觉了。
这通常意味着组件开始承担不属于自己的变化。
好组件不是永远不变,而是变化仍然在边界内。
写在最后
组件设计真正难的地方,不是怎么把代码拆小,而是怎么把职责拆对。
能复用只是起点,真好用的组件还要满足:
- 边界明确
- 参数克制
- 组合自然
- 不被业务逻辑绑死
只有这样,组件库才会越用越轻,而不是越积越重。