🎧 Listen to this article: हिंदी · English · தமிழ் · తెలుగు · ಕನ್ನಡ · മലയാളം · ଓଡ଼ିଆ · 日本語 · 中文
🌍 Read this in your language: हिंदी · தமிழ் · తెలుగు · ಕನ್ನಡ · മലയാളം · ଓଡ଼ିଆ · 日本語 · 中文
引发这一切的标题
现在各地的代码库中都在发生这样的对话。有人提议增加一个新的 React 组件。这看起来似乎无害。但随后另一个人问道:我们真的需要这个吗?答案通常让人惊讶。
在2026年7月14日发表的一篇文章中,DEV Community 的一位开发者描述了他们代码库中的一次架构讨论。几个同事想引入一个新的 Heading 组件。代码看起来会是这样的:
<Heading level={3}>Profile Settings</Heading>
它很干净。具有语义化。感觉井井有条。但这里存在一个问题。
我们已经有了三种解决方案
团队已经可以这样写:
<h3>Profile Settings</h3>
而且他们也可以使用现有的、专为此类灵活性而构建的 Text 组件:
<Text as="h3">Profile Settings</Text>
那么为什么要添加第四种方法呢?当被问及此事时,回答很诚实:“我喜欢我们有一个单独的 heading 组件。”
这是一个人类层面的原因。这不是一个坏理由。但它不是一个工程层面的原因。
个人偏好不是设计原则
前端团队热爱抽象。有时候有些过了头。包装器被进一步包装。在这些之上的包装器再次被包装。最初的 HTML 变得面目全非。
这些辩护听起来很熟悉:“它看起来更干净。”“感觉更一致。”“我喜欢它。”
这些是可以理解的。它们能引起共鸣。但它们是软弱的工程理由。原因如下:每一个新组件都要付出代价。
- 要编写的文档
- 要维护的代码
- 要保持最新的测试
- 要培训的新开发者
- 当事物变化时的迁移路径
- 让团队困惑的 API 重叠
共享的代码库不是任何人的个人沙盒。每一个抽象都会成为你的团队长期负担的一部分。这种负担需要比偏好更有力的理由。
HTML 已经是一个精心设计的 API
我们有时会忘记这一点。HTML 不是原始的。它并不粗糙。它是一个经过深思熟虑的工程化抽象,你可以在其之上构建——但并不总是要取代它。
以标题为例。原生的层级结构已经为你提供了:
- 语义含义(h1 比 h3 更重要)
- 内置的可访问性支持
- 屏幕阅读器兼容性
- SEO 上下文
- 文档大纲结构
所有这些都不是副作用。它是被设计进标准中的。
当你像这样把它包装在一个自定义组件中时:
<Heading level={1}>Dashboard</Heading>
<Heading level={2}>Analytics</Heading>
<Heading level={3}>Revenue</Heading>
在大多数情况下,你并没有改善输出。你只是在改变语法。而在不改变行为的情况下改变语法,很少值得付出成本。
改变一切的一条规则
以下是应指导这些决定的原则: 不要抽象语法。要抽象行为。
就是这样。这一个区分消灭了大多数不必要的争论。
一个糟糕的抽象过于模仿原生的 HTML:
<Heading level={3}>Title</Heading>
为什么这很糟糕?因为它主要模仿了 h3。它没有添加任何新东西。
一个好的抽象添加了真正的行为:
<Text variant="muted" size="small" truncate>
Description
</Text>
这集中了:
- 设计令牌映射
- 文本截断逻辑
- 主题一致性
- 响应式行为
这就是价值。这值得付出抽象成本。
一个概念应该拥有一个 API
困惑在代码库中传播得很快。这里是一个完美的滋生地:
<Heading level={3}>Profile</Heading>
<Text as="h3">Profile</Text>
<h3>Profile</h3>
编写同一件事的三种方式。现在每一个拉取请求都会变成一场风格辩论。“我们应该使用 Heading 还是 Text 还是原生的 h3?”没人真正知道。
优秀的系统减少选择。一个有用的原则: 一个概念,一个 API。 如果 HTML 已经处理了语义,那就让它掌管语义。如果你的 Text 组件掌管排版,那就让它掌管排版。清晰的分离。清晰的所有权。
组合胜过抽象
与其为特性的每一种组合创建一个包装器,不如优先使用组合。
避免这样:
<Heading level={2}>Billing Settings</Heading>
更推荐这样:
<h2>
<Text variant="secondary">
Billing Settings
</Text>
</h2>
你得到了:
- 原生 HTML 语义(h2 依然意味着 h2 的意思)
- 可重用的排版令牌(Text 变体处理样式)
- 清晰的责任(h2 负责结构,Text 负责外观)
- 没有重叠的 API
- 没有歧义
比起另一层抽象,组合更加灵活并且增加了更少的困惑。
构建前的五个问题
在提出一个新组件之前,问问自己:
1. 这实际上解决了什么问题? 不要说“看起来更干净”或“我喜欢这样。”而要是一些可衡量的东西,比如“强制实施可访问的层级结构”或“防止字体大小不一致。”
2. HTML 已经解决它了吗? 如果是:为什么要取代它?那种负担需要认真的理由。
3. 它减少了复杂性吗? 或者它只是将复杂性转移并隐藏在一个组件中?
4. 它会引入 API 重叠吗? 重叠的做事方式会造成不一致的代码库。那是一种成本,而不是一项特性。
5. 它增加了行为吗? 如果该组件只是改变了语法而没有增加行为,它可能不应该存在。
结论
不是每一个重复的模式都值得拥有一个组件。不是每一个 HTML 元素都需要一个 React 包装器。并且不是每一种偏好都值得成为一个共享的抽象。真正重要的问题是:这个抽象是在创造新能力,还是仅仅是新语法?诚实地回答这个问题,你就会保护你的代码库免受多年来无人真正需要的复杂性的困扰。
优点
- 为决定组件何时解决真正问题提供了可衡量的标准
- 保留了原生 HTML 元素的可访问性和语义价值
- 减少了因完成相同任务的多种方式而引起的团队困惑
- 将组件创建的重点放在增加新行为上,而不仅仅是改变语法
- 鼓励组合,这比包装器抽象更灵活
- 有助于防止代码库被掩埋在一层层不必要的组件之下
缺点
- 更喜欢构建自定义抽象的开发者可能会觉得这种方法具有限制性
- 已经拥有许多自定义组件的代码库面临着重大的重构工作
- 确定什么算作“行为”与“语法”需要持续的团队讨论和共识
- 组合模式有时让人感觉不如层次化的组件结构井然有序
注意
本文解释了来自已发表来源的架构原则。代码示例仅为说明性,并使用占位符 JSX 语法。在将这些原则应用于你自己的代码库之前,请针对原始来源验证这些声明,并将该指南适应于你团队的特定背景和现有模式。每个团队的情况都不同;对一个团队有效的方法可能需要为另一个团队进行调整。应始终与你的团队讨论架构决策,而不是单方面地应用规则。
常见问题
什么时候我应该创建自定义组件而不是使用 HTML? — 当组件增加诸如设计令牌、响应式逻辑或集中样式等行为时,创建它们。如果组件仅改变语法而不添加逻辑,通常原生 HTML 或组合效果更好。
我如何区分好的抽象和坏的抽象? — 问一问:这个组件是否解决了 HTML 尚未解决的问题?它是否集中了行为或逻辑?如果是,它可能是好的。如果它只是包装 HTML 而没有增加能力,它可能是不必要的。
如果我的团队已经有太多自定义组件怎么办? — 从识别哪些组件增加了实际行为、哪些只是包装 HTML 语法开始。保留那些解决真正问题的组件。对于仅有语法的包装器,逐步迁移到原生 HTML 或组合。
为什么组合比包装器组件更好? — 组合保留了原生的 HTML 语义,清晰地重用了设计令牌,并保持了关注点分离。它也更灵活,因为你可以将许多聚焦的小部件结合起来,而不是被锁定在一个包装器所允许的内容中。
关于设计系统一致性呢? — 设计系统应该定义行为和设计令牌(颜色、字体、间距),而不是创建包装器。让开发者用原生元素或最小的抽象来组合那些令牌。
我如何向我的团队解释这种方法? — 将其作为针对未来组件的决策框架,而不是对现有组件的批评。重点关注每一个组件所带来的成本:文档、测试和维护。就什么样的行为能够证明添加新组件是合理的达成一致。
标签
#react #frontend #architecture #components #webdev #designsystems #abstraction #codemaintenance
API Security Testing Checklist
A practical workflow for testing authentication, authorization, input handling, business logic, and evidence without losing track of scope.
Free. No spam — unsubscribe in one click.


Responses
Sign in to leave a response.