别再“过度设计”你的组件了:小团队的生存哲学

4 阅读4分钟

1. 惯性思维的陷阱

最近在带领一个小团队开发组件库时,我突然意识到自己掉进了一个“高级陷阱”。

由于之前在大厂、大团队打磨过跨部门使用的通用组件,我的肌肉记忆告诉我:一个好的组件必须无懈可击。 我下意识地为每一个按钮留了 5 个插槽,为每一个弹窗设计了 10 多个 API 接口,甚至连一个简单的内边距(Spacing)都想通过变量让外部去控制。

但当我看着同事们对着满屏的文档发愣,而业务进度却停滞不前时,我开始反思:在只有几个人的团队里,我真的需要造一架波音 747 吗?

2. 过度设计的“四宗罪”

当我们试图用“大厂规范”去套用“小团队业务”时,问题接踵而至:

  • 时间成本的浪费: 你花了 3 天时间去写一个“支持 100 种场景”的复杂组件,但实际上业务里 99% 的时间只用最基础的那一种。
  • 维护成本的激增: API 越多,逻辑分支就越多。每一次底层依赖升级,你都要回过头去处理那些“为了万一”而留下的冗余逻辑。
  • 使用体验的倒退: 对于使用者来说,简单的 Button 变成了需要配置一堆 props 的黑盒。当灵活性高到一定程度,它反而变得难以驾驭。
  • 团队理解的鸿沟: 小团队需要的是快速上手。当你设计了一个极其抽象的组件,却需要开一小时会去讲解它的实现逻辑时,这个设计就已经失败了。

3. “去过度化”重构:从复杂回归务实

为了更直观地说明,我们来看看在现代 UI 框架中,如何通过“做减法”来提升开发效率。

案例一:React 弹窗组件(组合优于配置)

❌ 过度设计:参数地狱

试图通过 Props 掌控一切,导致组件内部逻辑臃肿。

TypeScript

// 别这么做:为了灵活性暴露了太多配置项
<MyGlobalModal
  title="确认删除"
  onConfirm={handleDelete}
  confirmText="确定"
  cancelText="取消"
  titleColor="#FF4D4F"
  paddingSize="large"
  headerSlot={<CustomHeader />} 
  isCentered={true}
/>

✅ 务实重构:利用 children 组合

只提供容器,把内容的决定权交还给业务页面。

TypeScript

const Modal = ({ isOpen, onClose, children }) => {
  if (!isOpen) return null;
  return (
    <div className="modal-overlay">
      <div className="modal-content">
        <button className="close-btn" onClick={onClose}>×</button>
        {children} {/* 核心:内容由外部自由组合 */}
      </div>
    </div>
  );
};

// 业务使用:间距、按钮排列直接在业务层决定,简单透明
<Modal isOpen={show} onClose={hide}>
  <h3 className="text-red-500">确认删除</h3>
  <p>一旦删除无法恢复。</p>
  <div className="flex justify-end gap-2 mt-4">
    <button onClick={hide}>取消</button>
    <button className="btn-danger" onClick={confirm}>确定</button>
  </div>
</Modal>

案例二:Jetpack Compose 布局(拥抱规范,减少变量)

❌ 过度设计:变量式布局

为一个简单的信息行设计了极其精细的配置。

Kotlin

@Composable
fun InfoRow(
    label: String,
    value: String,
    labelWidth: Dp = 80.dp, // 过度设计:暴露了不必要的视觉细节
    spacing: Dp = 16.dp,
    labelColor: Color = Color.Gray
) {
    Row(modifier = Modifier.padding(spacing)) {
        Text(text = label, modifier = Modifier.width(labelWidth), color = labelColor)
        Text(text = value)
    }
}

✅ 务实重构:硬编码即规范

如果团队 UI 规范是统一的,那就直接写死那些“不会变”的东西。

Kotlin

@Composable
fun InfoRow(label: String, value: String) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 12.dp, horizontal = 16.dp) // 直接根据 UI 规范写死
    ) {
        Text(
            text = label, 
            modifier = Modifier.width(100.dp), // 既然是规范,就没必要传参
            style = AppTypography.labelStyle 
        )
        Text(text = value, style = AppTypography.valueStyle)
    }
}
  • 重构点: 删掉所有布局参数。如果 UI 规范变了,你只需要在组件内部改一次,而不是搜遍全局去改那些零散的参数传值。

4. 如何判断是否“过度”?

在动手写代码前,先问自己三个问题:

  1. 这个 Prop 是否有 3 个以上的页面在用? 如果只有一个页面用,直接写死,或者通过组合(children/slot)解决。
  2. 增加这个 API 是否是为了“预防”未来的变化? 如果是,立刻删掉。YAGNI (You Ain't Gonna Need It) 永远是小团队的金科玉律。
  3. 如果 UI 规范改了,我要改 1 个地方还是 10 个地方? 如果为了追求所谓的“灵活性”导致修改成本激增,那就是本末倒置。

5. 结论:不要为“想象中”的需求买单

架构的本质是平衡,而非完美。

在大厂,组件是产品,需要考虑极致的通用性;在小团队,组件是工具,需要考虑的是生产力。

  • 刚好够用: 只解决当下的问题,不预设三年后的需求。
  • 清晰胜过灵活: 代码应该是那种一眼就能看出在干什么的,而不是需要翻阅文档才能理解的迷宫。
  • 拥抱删减: 一个 API 如果三个月没被用过,就该考虑它的去留。

别再沉迷于那些花哨的接口和精巧的解耦了。回头看看你的业务,看看你的队友——最好的设计,往往是那个能让他们早点下班、少写点文档、一眼就能看懂的代码。


后记:

这篇文章并非否定封装,而是提倡**“适度封装”**。在快速迭代的环境下,冗余的灵活性往往是沉重的技术债。