重构火葬场?3个原则,把前端组件拆解效率提50%

两年前接手过一个后台管理系统,那简直是一场灾难。

当时有个需求很简单:把所有页面的“导出Excel”按钮颜色从蓝色改成绿色。结果,团队里的两个小伙伴整整改了一天半。为什么?因为这个按钮在40多个页面里是直接复制粘贴的代码,而且有的页面写死在HTML里,有的封装了一半,有的甚至跟具体的业务接口绑死在一起。

那时我才深刻意识到:很多中小团队所谓的“组件化”,其实只是“把代码挪个位置”而已。

如果你觉得组件化就是为了“复用”,那你大概率还在踩坑。对于中小团队,组件化的核心价值只有两个:降低认知负荷隔离变更风险

今天分享我在一线“填坑”多年总结的拆解思路,不整虚头巴脑的理论,只聊怎么落地。

一、 警惕“上帝组件”:别把配置项当万能药

这是新手最容易犯的错,也是我每周五代码Review时“杀”得最狠的一类代码。

观点: 组件的灵活性不是靠无限增加 Props(属性)来实现的,而是靠组合。

真实案例: 去年我们组招了个挺机灵的小伙子,叫小张。他为了体现“封装能力”,搞了一个通用的 ListTable 组件。起初挺好用,只需传个数据数组进去。

两周后,噩梦开始了:

  • 需求A:有的列要加红,小张加了 renderRed 属性;
  • 需求B:表头要支持点击排序,小张加了 sortableonSort
  • 需求C:第一列要加复选框,但有时候是单选,他又加了 selectionType

三个月后,这个 ListTable 变成了拥有 30多个 Props、内部充满 if-else 判断的“上帝组件”。任何人都没法维护,因为改一个属性,可能会炸掉其他五个页面的展示。

硬核解法:控制反转(Inversion of Control)

别试图在组件内部预判所有场景。把“怎么展示”的权利交还给调用者。

思考题: 你的项目里有没有那种超过200行代码、Props 超过10个的基础组件?如果有,它就是那个随时会爆的雷。

代码对比:

错误的上帝模式:

// 这种组件写出来就是为了折磨队友的
<ListTable
  data={list}
  showCheckbox={true}
  checkboxType="radio"
  enableSort={true}
  headerColor="blue"
  // ...后面还有20个属性
/>

推荐的组合模式(Slot/Children):

// 基础组件只管布局和样式,内容由外部决定
<Table>
  <TableHead>
    <Checkbox />
    <SortIcon />
  </TableHead>
  <TableBody>
    {list.map(item => (
       <Row>
         <Cell>{item.name}</Cell>
         {/* 特殊逻辑直接在这里写,不用侵入组件内部 */}
         <Cell style=>
            {item.status}
         </Cell>
       </Row>
    ))}
  </TableBody>
</Table>

当你发现一个组件需要传 styleObject 或者 className 进去覆盖默认样式时,通常说明拆解粒度不够,或者封装得太死了。

二、 拒绝“业务入侵”:让UI组件变“哑巴”

这是造成项目难以维护的头号杀手。很多项目经理抱怨:“为什么换个接口,前端要改两天?”多半是因为业务逻辑渗透到了UI组件里。

观点: UI组件应该是“哑巴”,只负责“画图”和“喊话”(触发事件),绝对不能知道数据是从哪里来的。

真实案例: 我们曾经有个“用户卡片”组件。最开始是在CRM系统里用的,代码里直接引入了 fetchUserDetail(id) 这个API请求。

后来做营销活动页,产品经理想复用这个卡片样式,展示中奖用户信息。结果发现复用不了——因为营销活动的数据结构跟CRM完全不一样,而且不需要调那个API。

结果就是:前端不得不Ctrl+C、Ctrl+V,复制了一份代码改名叫 WinnerCard。随着版本迭代,两份代码样式逐渐不一致,UI走查天天报Bug。

硬核解法:容器组件 vs 展示组件

这个方法我用了3年,百试百灵。强制要求团队把组件分为两类:

  1. 展示组件(Dumb Component):

    • 只收数据(Props),不调接口。
    • 不知道业务逻辑,只负责长得好看。
    • 放在 components/ui 目录下。
  2. 容器组件(Smart Component):

    • 负责调接口、处理数据转换。
    • 把处理好的纯数据传给展示组件。
    • 放在 components/featurespages 目录下。

配图

实操步骤: 当你写代码时,如果发现在一个通用按钮里引入了 store 或者 axios,请立刻停手。你应该在外面包一层,把点击事件抛出来。

“展示组件就像个没头脑的画师,容器组件才是那个操心的管家。”

三、 别做“预言家”:遵循“三次法则”

很多有追求的开发人员(包括曾经的我)都有洁癖,写第一遍代码时就想把它封装得完美无缺,支持未来可能出现的各种需求。

配图

观点: 过早优化是万恶之源。在重复出现第三次之前,允许复制粘贴。

真实案例: 去年做个活动页,有个倒计时功能。有个兄弟觉得以后肯定常用,于是花了半天时间封装了一个巨牛逼的 CountDown:支持毫秒级、支持服务器时间校准、支持自定义格式化字符串、支持倒计时结束回调…

结果呢?那个活动上线3天就下线了。那个巨复杂的倒计时组件再也没人用过,还留在那占体积。反而是后来另一个活动需要简单的“天:时:分”,大家嫌那个组件太重,又重写了一个。

硬核解法:Rule of Three(三次法则)

  1. 第一次: 只管写业务,代码直接写在页面文件里,怎么快怎么来。
  2. 第二次: 当遇到相似场景,允许复制粘贴(Copy-Paste),稍微改改。
  3. 第三次: 当你发现自己在复制第三次时,这时候你已经非常清楚共性是什么、差异是什么了。此刻,才是重构和提取组件的最佳时机。

配图

不要为了“可能”的需求去写代码,只为“当前”的痛点去封装。

总结与行动

组件化拆解不是为了炫技,而是为了让队友少骂你两句,为了下班能准时走

回顾一下核心思路:

  1. 别搞上帝组件:用组合替代配置,属性越少越好。
  2. 别让业务入侵:UI归UI,数据归数据,把API请求从UI组件里踢出去。
  3. 别做预言家:忍住封装的冲动,直到代码重复了三次。

给你的落地建议(Action Plan):

  1. 本周自查: 打开你的项目,找到那个Props最多的组件,尝试用“组合模式”重写一个简单的Demo,对比一下灵活性。
  2. 清理逻辑: 检查公共组件库,凡是里面包含 axiosfetch 或者特定业务状态判断的,标记出来,下次迭代计划拆分。
  3. 制定红线: 在团队里定个死规矩——通用UI组件禁止包含任何业务接口调用。

最后想问问你: 在你现在的项目里,修改一个通用的顶部导航栏,需要改动几个文件?如果超过1个,或许你该试试上面的方法了。