兴趣岛
前端

前端组件化设计思路

2026/4/30

前端组件化设计思路

我是怎么被“复制粘贴”逼疯的

刚入行那会儿,写页面靠的是“快”。需求一来,先在文件夹里翻有没有现成的页面,像拼乐高一样把几块代码复制过来,改个类名、调一下接口地址,页面就能跑。那段时间的成就感很高:今天上线三个活动页,明天改两套弹窗,感觉自己像个万能的裁缝。可时间一久,事情开始反噬:活动页的弹窗和产品页的弹窗长得差不多,却各自维护一套逻辑;改一个按钮颜色,要在五个文件里找;新同事问我“这个交互是不是已经做过了”,我只能尴尬地说“好像有,也可能没有”。最离谱的一次,是上线前发现两个地方的下拉框提交逻辑不一致,一个走GET一个走POST,结果测出bug时已经半夜。那一刻我才意识到,快的前提不是复制得快,而是知道什么东西值得复用、什么东西应该被封装。

把“长得像”变成“是同一个”

后来我开始强行给自己立规矩:只要在一个项目里出现两次的东西,就要停下来想想能不能做成组件。最早是从按钮开始的。一个项目里同时存在“主要按钮”“次要按钮”“危险按钮”,样式靠的是各自写一套类名,行为靠的是各自绑事件。后来我把颜色、尺寸、禁用状态、加载状态统一收口到组件内部,用 props 控制差异。比如同样是提交,加载时统一展示旋转图标,而不是让每个页面自己写个 spinner。组件写好后,第一次用确实要多花点时间:得想清楚参数怎么命名、默认值是什么、要不要透出事件。但第二次再用时,就只剩下“写标签”的成本。样式和行为被锁在组件内部,改需求时只要改一个地方,整站风格就能统一。更重要的是,新同事不用猜“这个按钮能不能点”,看 disabled 和 loading 两个属性就够。

组件不是“拆得越碎越好”

有一段时间我走偏了,觉得组件化就是把页面拆得越细越厉害。结果一个表单被拆成输入框、提示文字、错误状态、校验规则、提交按钮五个组件,层层嵌套,像俄罗斯套娃。调试时要在五六个文件之间来回跳转,一个数据流绕来绕去,最后发现只是少传了一个 prop。更糟的是,有些组件只在某一个页面出现,却因为“可能被复用”而被强行独立,导致项目里堆了一堆半成品,维护成本反而更高。后来我调整了判断标准:组件是否稳定、是否承载明确的职责、是否在不同场景下保持行为一致。比如一个“用户头像+昵称”的组合,如果在三个页面以上以相同方式出现,就值得封装;如果只是某个业务卡片里长这样,那就先写在页面里,等它真的“长大”了再拆。组件化的终点不是文件数量,而是认知成本的降低。

让组件在时间里“活下来”

组件一旦被更多人使用,就会暴露真实世界的复杂性。最初设计的“表格组件”只支持静态列和分页,后来业务方要求支持树形数据、固定列、列宽拖拽、单元格编辑。每加一个功能,props 就多几个,条件判断就多一层,组件开始变得臃肿。这时候如果继续往里堆,它就会变成一个谁都不敢动的“巨石”。我学到的办法是把能力分层:核心只负责渲染数据和发出事件;扩展能力通过插槽、渲染函数或组合式工具提供;复杂场景用多个小组件协作完成,而不是让一个大组件包打天下。同时,文档和示例要和组件一起更新,最好有“推荐用法”和“反例”。我见过太多组件库死于“只有 API 没有场景”,开发者在参数列表里迷失,不知道什么时候该用它。组件能不能活下来,不取决于一开始设计得多完美,而取决于它是否能在变化中保持边界清晰。

设计思路落地的几条笨办法

做组件化并不需要一开始就推倒重来,我习惯用一些“笨办法”慢慢把秩序建立起来。第一,给组件起名字时,尽量用名词而不是动作,比如“dialog”而不是“openDialog”,动作留给事件。名字一旦定下来,就别轻易换。第二,写组件之前先写三个使用示例,看看参数会不会多到离谱。第三,用 TypeScript 不是为了炫技,而是让“能不能用”在写代码时就能报错。第四,区分容器组件和展示组件:前者管数据和流程,后者只管渲染和交互。第五,建立一个“组件准入”的小流程:新组件需要有人用、有文档、有测试,才算完成。这些事看起来琐碎,却能在团队里形成一种默契:不是所有代码都值得被保留,也不是所有复用都叫组件化。

结语

前端组件化本质上是一场和复杂度的谈判。我们总希望用更少的认知承担更多的变化,却常常在“灵活”和“可控”之间摇摆。经历过复制粘贴的快感,也踩过过度拆分的坑,我逐渐明白,组件化不是为了写出更炫的代码,而是为了让项目在时间中保持可理解、可修改、可协作。当一个按钮、一个弹窗、一张表格,能在不同的需求里稳定地完成自己的任务,而不制造新的混乱时,组件才真正活了下来。而我们作为设计者和实现者,也才有余力去思考更重要的事:如何把问题本身变得更简单。