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. 如何判断是否“过度”?
在动手写代码前,先问自己三个问题:
- 这个 Prop 是否有 3 个以上的页面在用? 如果只有一个页面用,直接写死,或者通过组合(children/slot)解决。
- 增加这个 API 是否是为了“预防”未来的变化? 如果是,立刻删掉。YAGNI (You Ain't Gonna Need It) 永远是小团队的金科玉律。
- 如果 UI 规范改了,我要改 1 个地方还是 10 个地方? 如果为了追求所谓的“灵活性”导致修改成本激增,那就是本末倒置。
5. 结论:不要为“想象中”的需求买单
架构的本质是平衡,而非完美。
在大厂,组件是产品,需要考虑极致的通用性;在小团队,组件是工具,需要考虑的是生产力。
- 刚好够用: 只解决当下的问题,不预设三年后的需求。
- 清晰胜过灵活: 代码应该是那种一眼就能看出在干什么的,而不是需要翻阅文档才能理解的迷宫。
- 拥抱删减: 一个 API 如果三个月没被用过,就该考虑它的去留。
别再沉迷于那些花哨的接口和精巧的解耦了。回头看看你的业务,看看你的队友——最好的设计,往往是那个能让他们早点下班、少写点文档、一眼就能看懂的代码。
后记:
这篇文章并非否定封装,而是提倡**“适度封装”**。在快速迭代的环境下,冗余的灵活性往往是沉重的技术债。