本节摘要:组件是自包含、接口清晰、可独立复用的 UI 单元,是 React 和 Vue 共同的基本构建块。本节讲"该不该拆组件、拆到多细"的判断标准,并给出一套可操作的评审清单——这正是"比稿现场"里每个方案最先过的那一关。
本节阅读目标
阅读完本节,你应当能够:
看一个只有三五个页面的小应用,组件化的收益几乎看不见;可应用一旦到了几十个页面、十来个开发并行,没有组件化,任何一个改动都会像推倒多米诺骨牌。组件把"改一处影响全局"压制成"只影响它自己",这是它成为现代前端地基的根本原因。
组件之所以是组件,靠三个属性撑起来,缺一不可:
一个典型的 React 组件长这样,注意它是如何同时照顾三个属性的:
function Counter({ step = 1, onChange }) { const [value, setValue] = useState(0); const increment = () => { const next = value + step; setValue(next); onChange?.(next); // 把"变化"通过事件抛给父级,而不是自己越界改父级状态 }; return ( <div> 当前值:{value} <button onClick={increment}>加 {step}</button> </div> ); }
这里 props(step、onChange)是接口,value 是自包含的内部状态,事件回调是它对外的通信出口。三者齐备,组件才"端着清清楚楚的门面"。
给任何"要不要拆成组件"的方案做评审,套三个问题就够:
沿这三问往下推,很容易避开两个极端。
比稿最常见的滑铁卢不是能力不足,而是走极端。
欠拆分的症状:一个巨型组件塞了几百行,props 二十多个,注释写着"别动这里我也不确定"。治理方向是顺着职责把大块切开。
过度拆分的症状:连一行文字都要包一层组件,props 层层透传,接口数比内容还多。治理方向是合并那些"永远相伴"的小块,别让接口网络失控。
下面用一个矩阵概括"一口吞 vs 切成末"的取舍,这是比稿评审的核心一张表。
| 情形 | 欠拆分 | 过度拆分 |
|---|---|---|
| 典型信号 | 一个组件几百行 | 一行内容也独立成组件 |
| 接口 | props 多到看不懂 | props 透传几层 |
| 代价 | 改动波及面大 | 理解与调试成本高 |
| 发力方向 | 按职责切开 | 合并永远相伴的块 |
组件化思想是整套教程的起点和归处:第 3、4 章把"怎么写出一个组件"落到 React/Vue 的具体语法;第 5 章把它推进到"多个组件怎么往来、怎么复用"。投资在 2.1 的这一课会反复兑现。
这里再把"组件"与另外两个易混概念划个清楚,常被新手放在一起比较。**组件(Component)**是带界面与行为的可复用单元,是本章的主角;**模块(Module)**通常指按文件边界切分的代码单位,偏工程组织而非界面单元,一个组件内部可以由多个模块拼成;函数是最细的复用原子,组件靠函数来组织行为,但函数本身不承担"自包含 UI 单元"这层职责。三者边界常常互相包含,分清楚它们,才能在评审接口时对"该抽象到哪一层"心里有数。
拿一个互联网常见的例子走一遍评审:一个"用户卡片",展示头像、名字、最近动态,还有一个"关注/取关"按钮。三种方案摆上桌。
方案甲:一个 UserCard 组件打包全部。写得快,但名字要显示的地方(比如消息列表)没法复用,因为整块内容都耦合在卡片里。评审三问里"独立吗"这一问就卡住了。
方案乙:拆成 UserAvatar、UserName、UserAction(关注按钮)、UserFeed 四个小件,再拼成 UserCard。复用性最好,但刚起步时接口网络有点密,属可接受的代价。
方案丙:全站几十个小组件,连头像都再拆成"图片+占位文案"。过度细,接口数比内容还多,读起来反而费劲。
评审结论通常是乙。它的取舍是:在"可复用"与"接口简洁"之间取一个甜点,而不是贪多。这就是组件化的判断功夫——不是会拆,而是拆得恰到好处。这也正是全集用比稿现场贯穿的原因:多数决策没有标准答案,只有经过三问评审后的相对最优。
给组件的对外接口(props 与事件)定几条底线,能少走很多弯路:
这些纪律在比稿现场会反复使用:同一场景不同方案的评审,最后都会落到"接口是否清晰、职责是否单一"这两条上面来。
下一节进入渲染机制的里层——虚拟 DOM。组件是面子,虚拟 DOM 和 diff 才是让"声明式"高效落地的里子。