前端组件设计实践:从能复用到真好用,中间差了什么

0 阅读

前端项目里经常会强调组件化。

按钮做成组件,弹窗做成组件,表单项做成组件,最后看起来页面被拆得很细,代码也“似乎”更复用了。

但实际开发几年后,不少团队都会发现一个尴尬的问题:组件确实越来越多,可真正好用、敢复用、改起来不痛苦的组件却没有同步增加。

说到底,组件化不等于组件设计。

组件“能用”不代表“可维护”

很多组件第一次写出来时都很好用,因为它们只服务一个页面、一个场景、一个需求。

问题往往出在第二次、第三次复用时。

这时常见情况是:

  • 新场景需要一个额外参数
  • 旧交互要兼容新的样式
  • 一个组件同时承担多个职责
  • 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 增长到一定程度后,维护成本会明显上升。

因为这意味着:

  • 使用者需要记住更多规则
  • 参数组合变复杂
  • 组件内部状态分支增多

这时比起继续加参数,更值得做的是回头判断:

  1. 组件职责是不是混了
  2. 是否应该拆成两个组件
  3. 是否应该把部分逻辑交给插槽或 children

组合往往比继承和配置更稳

前端组件里,最稳的复用方式通常不是一个万能组件,而是小组件组合。

例如一个卡片组件可以拆成:

  • Card
  • CardHeader
  • CardContent
  • CardFooter

这样每一部分都更明确,也更容易适配不同页面,而不是所有变化都靠布尔参数来开关。

样式统一要靠规范,不要只靠组件兜底

很多人希望通过组件库统一所有风格,这方向没错,但不能把所有规范都压在组件本身上。

如果团队没有基础设计规范,例如:

  • 间距体系
  • 字号层级
  • 颜色语义
  • 交互反馈规则

那组件只会变成“风格打补丁”的地方。

一个简单的判断标准

如果你在给组件加某个功能时,需要写一句“为了兼容另一个页面”,那就应该警觉了。

这通常意味着组件开始承担不属于自己的变化。

好组件不是永远不变,而是变化仍然在边界内。

写在最后

组件设计真正难的地方,不是怎么把代码拆小,而是怎么把职责拆对。

能复用只是起点,真好用的组件还要满足:

  • 边界明确
  • 参数克制
  • 组合自然
  • 不被业务逻辑绑死

只有这样,组件库才会越用越轻,而不是越积越重。