探讨 Jordan Walke 的 React 如何引入可复用组件、声明式视图与状态驱动渲染,从而重塑现代前端架构。

Jordan Walke 是一名软件工程师,最广为人知的成就是在 Facebook 工作期间创建了 React。在 React 出现之前,前端通常围绕页面、模板以及越来越多试图把 HTML、CSS 和 JavaScript 同步在一起的“胶水代码”构建。Walke 的关键思想是颠倒模型:不要把 UI 当作不断被打补丁的文档,而是把它当作一棵由小型、可复用组件组成的树,通过组合这些组件来构建更大的功能。
这不仅仅是一个新库——而是一种新的 UI 思考方式。组件将一段界面与它所需的逻辑和状态打包,然后通过清晰的接口(props)向应用其它部分暴露。这让构建界面更像用乐高搭积木,而不是编辑单一易碎页面。
React 之所以重要,在于它帮助团队:
我们将逐步讲解让 React 有影响力的实用思想:
你不需要是框架专家来理解这些内容。目标是把心智模型讲清楚——这样你就能识别好的 React 模式、避免常见误解,并把相同原则应用到 React 之外的场景中。
在 React 出现之前,很多团队通过拼接模板、jQuery 风格的 DOM 操作以及越来越多的“当 X 发生,更新 Y”规则来构建复杂界面。这在界面不复杂时是可行的,但一旦界面变得繁忙,问题就出现了。
常见模式是:获取数据、渲染 HTML,然后附加事件处理器直接改变 DOM。当状态发生变化(新增项、校验错误、切换)时,就要有人记住所有依赖该状态的位置。
这会导致诸如:
随着页面演进,相同的业务规则会重复出现在多个处理器中:"如果字段为空就禁用按钮"、"高亮未读项"、"没有结果时显示空状态"。当需求改变时,你不得不到处搜索并更新每一份拷贝。
数据可以用几种清晰的结构建模:帖子列表、用户对象、过滤条件集合。然而 UI 会叠加多种状态组合:加载与已加载、错误与成功、已读与未读、编辑与查看、已过滤与未过滤——这些常常会同时出现。
想象一个新闻流:
如果没有像“UI 是状态的函数”这样的可预测规则,你最终会协调许多可能互相冲突的 DOM 修改。React 的目标是让更新变得可靠:改变数据/状态,UI 就会重新渲染以匹配——每次都是如此。
一个 组件 是一个可以命名、复用并单独推理的小型界面片段。简单说:组件接受 输入,并返回 在这些输入下界面应该是什么样子。
“输入 → 输出”的表述是组件模型的核心。不是把一个屏幕当成一个巨大的模板,而是把它拆成有目的的构建块——按钮、卡片、菜单、表单乃至整个区块——然后将它们组装起来。
在 React 中,最常见的输入是 props(属性的缩写)。props 是传入组件以配置它的值:文本、数字、标志、事件处理器,甚至其他 UI。
组件输出就是它渲染的 UI。如果 props 发生变化,组件可以生成不同的输出——无需你手动去寻找并更新 DOM。
例如,一个 Button 组件可能接受 label、disabled 和 onClick 等 props。一个 UserCard 可能接受 name、avatarUrl 和 status。你可以像阅读产品规格一样阅读组件的接口(props):“这个 UI 渲染正确需要什么?”
把 UI 拆成组件能很快见效:
Modal、Input 或 Dropdown 可以出现在多个页面。这与为每个页面复制并调整标记的做法形成鲜明对比。组件让重复变得不可接受,从而推动更可维护的实践。
React 鼓励以系统化的方式设计 UI:可组合的部分。一个“结账页面”变成组件树——CheckoutPage 包含 OrderSummary、ShippingForm 和 PaymentMethod。每个部分有清晰的输入和职责。
这种从组件出发的思考方式是 React 改变前端架构的主要原因之一。它为团队提供了一个共享的设计与开发单元:组件。
React 最大的心智转变是声明式 UI:你描述在某个状态下界面应是什么样子,React 负责在状态变化时更新页面。
你不必手动查找元素、修改文本、切换类或自己维护 DOM,同样的你只需关注 UI 的“形状”。当数据发生变化时,UI 被重新描述,React 负责找出最小的一组改动。
JSX 是一种便捷的方式,用类似 HTML 的语法在 JavaScript 中写组件结构。它并不是一个需要从零学起的新模板语言;它是一个“该组件渲染这个元素树”的简写。
关键好处在于:控制显示的标记和决定显示逻辑的代码在一起,这让组件在隔离时更容易理解。
命令式代码关注的是如何逐步更新 UI:
// Imperative: manually keep the DOM in sync
function setLoggedIn(isLoggedIn) {
const el = document.querySelector('#status');
el.textContent = isLoggedIn ? 'Welcome back' : 'Please sign in';
el.classList.toggle('ok', isLoggedIn);
el..(, !isLoggedIn);
}
声明式代码则关注当前状态下 UI 应该是什么:
function Status({ isLoggedIn }) {
return (
\u003cp className={isLoggedIn ? 'ok' : 'warn'}\u003e
{isLoggedIn ? 'Welcome back' : 'Please sign in'}
\u003c/p\u003e
);
}
因为渲染被表达为纯粹的描述,组件通常更易读、更易审查、也更容易重构。设计人员、以产品为导向的工程师或新同事通常能直接通过 JSX 理解组件,而无需在事件处理器和 DOM 变更中摸索。
这种清晰性改善了协作:UI 决策在一个地方可见,变更不太可能在界面其它地方制造隐藏副作用。
“状态”就是在用户交互期间会变化并且界面应反映的那些数据。它可能是搜索框中的当前文本、某个菜单是否打开、购物车里的商品,或网络请求的结果。如果它会变化并应影响显示,那它就是状态。
React 的关键举措是把渲染视为状态的结果,而不是一系列手工 DOM 步骤。你描述在给定状态下界面应是什么样子。当状态更新时,React 重新渲染相关部分。
这种心态不同于“找到元素,然后更新文本,然后切换类”。相反,你更新状态,界面自然更新,因为它是从状态派生出来的。
单向数据流意味着数据沿单一方向移动:
这减少了惊喜,因为你可以追踪一次更新的路径:事件发生、状态在某处改变、UI 从新状态重新渲染。少了“谁改了这个值?”的歧义。
function Counter() {
const [count, setCount] = React.useState(0);
return (
\u003cdiv\u003e
\u003cp\u003eCount: {count}\u003c/p\u003e
\u003cbutton onClick={() =\u003e setCount(count + 1)}\u003eAdd\u003c/button\u003e
\u003c/div\u003e
);
}
这里,count 是状态。点击按钮用 setCount 更新状态,React 随后重新渲染,段落显示新的数字。你从未直接“编辑 DOM”。
同样的模式可以扩展到过滤列表(状态 = 过滤文本,UI = 过滤后的条目)或表单校验(状态 = 字段值和错误,UI = 错误消息)。数据先变,视图只是结果。
React 的关键理念不是“重绘页面更快”。而是:把 UI 当作状态的结果,当状态变化时,比较“现在想要的 UI”与“之前的 UI”,然后只更新实际变化的部分。
当组件的 state 或 props 改变时,React 会再次调用你的组件以生成新的 UI 描述。把它想象成两张快照:
React 并不是清空 DOM 重建它,而是尝试计算出从 A 到 B 所需的最小 DOM 操作集合。
“虚拟 DOM”只是 React 的内存中 UI 表示——一个轻量的元素树(以及组件输出),描述了屏幕上应当显示的内容。它不是第二个浏览器或更快的 DOM,而是一个 React 能高效检查和比较的数据结构。
Reconciliation 是比较先前虚拟树与下一棵树以确定变化的过程。React 使用一些启发式规则来加速这个过程,例如:
<div> 不是 <span>)这并非魔法。性能取决于模式:稳定的 keys、避免不必要的重渲染、把组件工作量保持小、并且在渲染期间不要做昂贵的计算。React 能减少 DOM 抖动,但组件结构和数据流仍决定应用的流畅程度。
React 最大的扩展技巧不是某个功能开关或框架插件——而是组合:通过嵌套组件、通过 props 传递数据、以及使用 children 让组件“包裹”其它 UI 来构建页面。
当团队把组合作为常规做法,就不再把页面当作一次性产物,而是把它们看作可重新排列的小且可靠的部件,无需重写一切就能组装新界面。
children嵌套反映了 UI 的层次结构:页面包含区块,区块包含卡片,卡片包含按钮。props 是配置旋钮(文本、状态、回调)。children 允许组件提供结构,同时让调用方决定内部内容。
一个好的心智模型:props 用于定制、children 用于填充、嵌套用于组装。
定义结构和间距而不承担业务逻辑。例如:、、、。它们通常大量使用 ,这样相同布局可包裹不同屏幕。\n 统一表单行为与样式:、、。通过把标签、错误状态和校验消息集中管理,避免在多个页面重复。\n 保持重复 UI 的可预测性。常见划分是 (负责获取、分页、空状态)和 (负责单项的呈现)。这样更容易在不破坏数据处理的情况下更改渲染。
Hooks 是在不强制组件采用相同 UI 形态下复用有状态行为(切换、表单状态、数据获取等)的现代方法。这种分离帮助团队在保持逻辑一致的同时演化设计。
组合是设计系统保持一致性的基础:组件成为“被认可”的构建块,布局定义间距与层次规则。当系统更新——颜色、排版、交互状态——产品能以更少的人工修改继承改进。
状态只是会变化的数据。在 React 中,状态放在哪儿和它本身一样重要。
本地状态属于单个组件(或小部件),不需要被其它组件读取。想想:下拉是否打开、输入的当前值、哪个标签被选中。
把状态保持本地能减少协调成本并增强组件的可复用性。一个实用法则:如果只有一个组件关心这份状态,就不要把它导出到应用其它部分。
共享的应用状态是多个 UI 部分必须一致的数据。常见示例包括:
一旦多个组件需要相同的事实来源,重复状态会导致不匹配(“页眉显示 3 件,购物车页显示 2 件”)。
上移状态:把状态移动到最近的公共父组件并通过 props 传下去。这通常是最简单的选项,并且让数据流显式可见。\n Context:当许多组件需要相同值而不想做深度传参时很有用,例如主题或认证。Context 适合于相对稳定的全局关注点。\n 外部存储:当状态变得复杂(频繁更新、衍生数据多、跨页面工作流)时,专用的存储可以集中逻辑与更新。
当每一份状态都有清晰的拥有者时,React 的单向数据流发挥最大作用。尽量在可行时选择单一事实来源,并从它派生其它值(计数、总数、过滤后列表),而不是存储重复数据。
React 在日常开发中的最大收益不是渲染技巧,而是组件边界把 UI 工作分解为更小、更安全的变更。当组件有清晰职责和稳定的“公共表面”(props)时,团队可以重构内部实现而不强制重写整个应用。这种稳定性让代码审查更容易、降低意外破坏几率,并帮助新人更快理解改动位置。
一个有用的心智模型是:在给定 props 和 state 的情况下,组件应可预测地描述 UI。尽管存在副作用和浏览器 API,但大多数组件逻辑可以保持确定性。这就是为何可维护的 React 测试通常关注行为和输出:
无障碍检查在这里也很自然:如果你用角色和可访问名来测试,就能及早发现缺失标签、焦点问题和语义不一致。规则检查(lint、格式化、设计系统使用)强化同一思想:可预测的组件更易维护。
当组件暴露小型 props API 并隐藏实现细节时,多人可以并行工作:一个人调整样式,另一个改数据获取,第三个人更新测试——彼此不互相干扰。
React 性能通常不是“React 慢”,而是你的应用让浏览器做了多少工作。最快的 UI 是做最少事的 UI:更少的 DOM 节点、更少的布局/回流、更少昂贵计算、以及更少网络往返。
一个常见问题是 不必要的重渲染:小的状态变更导致大树重渲染,因为状态放得太高,或 props 每次都改变身份(内联创建的新对象/函数)。
另一个经典痛点是 大型列表——数百或数千行带图片、格式与事件处理器。即使每行“很便宜”,总量也会积累,滚动时浏览器跟不上就会出现卡顿。
先从结构入手:
同时关注用户感受:减少输入延迟、加速首屏有意义渲染、保持交互流畅。对频繁使用的交互优化 20ms 比对很少见屏幕节省 200ms 更重要。
派生状态 是可以从其它 state/props 计算出的数据(如用 firstName + lastName 得到 fullName,或用列表 + 查询得到过滤后的项)。存储派生状态常常造成 bug:你现在有两个可能会偏离的事实来源。
优先在渲染期间计算派生值(若计算昂贵则进行 memoization)。仅存储无法推导的数据——通常是用户输入、服务器响应和 UI 意图(如“面板是否打开”)。
React 不仅仅提供了一种更好写 UI 的方法;它促使团队重新组织前端的构建、共享和维护方式。在组件成为默认心智模型之前,许多项目把 UI 当作带有零散脚本和模板的页面来对待。随着 React 的普及,架构单位逐渐变为组件:一个具有清晰 API(props)和可预测行为的 UI 片段。
React 与单页应用(SPA)的兴起契合。当渲染由状态驱动时,“页面”不再是服务器交付的模板,而是组件组合加上客户端路由。因此代码常以功能区与可复用 UI 部件来组织,而不是以独立的 HTML 文件划分。
一旦 UI 由可复用片段构成,标准化这些片段就水到渠成。许多组织从复制粘贴标记转向构建组件库:按钮、表单控件、模态窗口、布局基元,和诸如空状态这样的模式。随着时间推移,这些库常演化为设计系统——共享组件加指南,让团队无需为每个页面重造界面即可保持一致体验。
组件鼓励团队用相同的名称表达相同概念。当每个人都谈论 <Button>、<Tooltip> 或 <CheckoutSummary> 时,讨论变得更具体:人们可以讨论行为与边界,而不仅仅是视觉效果。这样的共享词汇也加速新人通过代码发现系统并上手。
React 的成功改变了更广泛的前端社区对 UI 的思考方式:组件优先开发、声明式渲染和可预测的数据流成为普遍预期。其他框架即使实现细节不同,也纷纷采纳类似理念,因为这些实践在真实团队中更易扩展。
React 因让复杂 UI 更易演化而赢得声誉,但它并非“免费”的。事先了解权衡可以帮助团队为正确的理由采纳它,并避免盲目模仿。
React 有学习曲线:组件、hooks 以及像状态更新和副作用这类心智模型需要时间掌握。现代 React 也通常依赖构建工具链(打包、lint、TypeScript 常见但可选),这增加了设置和维护成本。最终,React 引入了抽象层级——组件库、路由、数据获取模式——这些能带来帮助,但也可能在出问题时隐藏复杂性。
“React 只是视图层。”理论上是,但实际上 React 强烈影响你的架构。组件边界、状态所有权与组合模式塑造数据流和代码组织方式。\n “虚拟 DOM 总是更快。”虚拟 DOM 更多关乎可预测更新和开发体验。React 可以很快,但性能取决于渲染模式、memoization、列表大小及避免不必要的重渲染。
React 非常适合具有大量交互状态、长期代码库和多人并行开发的应用。对于主要是静态的营销站点或少量小部件,简单方案(服务器渲染模板、轻量 JS 或最小框架)可能更容易交付和维护。
如果你在快速验证这些想法(组件边界、状态所有权、组合模式),一种体验式编码(vibe-coding)工作流会有帮助。例如,Koder.ai 允许你在聊天中描述功能并生成可运行的 React 前端加 Go/PostgreSQL 后端,然后用快照/回滚迭代并在准备好接手时导出源代码。这是一个在真正功能上验证架构决策的实用方法,而不必在一开始就投入完整构建。
下一步:原型化一个真实功能,衡量复杂度与团队产能,再有目的地扩展模式——而不是默认采用。
Jordan Walke 在 Facebook 工作期间创建了 React。React 之所以重要,是因为它用基于组件、状态驱动的方式替代了脆弱、依赖手工 DOM 操作的“胶水代码”,从而让复杂界面更容易扩展、调试和维护。
传统模板通常把 UI 规则分散到标记和零散的事件处理器中(“当 X 发生时,更新 Y”)。组件将 UI 与其逻辑捆绑在一起,并通过小而明确的接口(props)暴露出来,所以你可以用可预测的模块组合功能,而不是逐步修补页面。
组件是一个可复用的单元,它接受输入(通常是 props),并返回基于这些输入的 UI。
实用建议:
Props 是传入组件的配置项(文本、标志、回调或其他 UI)。把 props 当作契约:
disabled、onSubmit)而不是模糊的“百宝箱”对象声明式 UI 意味着你描述当前状态下界面应当是什么样子,而不是逐步说明如何更新 DOM。
实际做法:
JSX 是一种语法,让你在 JavaScript 中以类 HTML 的形式书写组件结构。它的好处是渲染逻辑和标记在同一处,使组件更容易阅读与审查。
状态是指在用户交互期间会发生变化并应当影响界面的任何数据(输入文本、加载状态、购物车条目等)。
实用规则:存储真实来源的数据(用户输入、服务器响应、UI 意图),其他可推导的值(计数、过滤后的列表)都从它们计算出来。
单向数据流的含义是:
这让调试更容易,因为更新路径是一条直线:事件 → 状态改变 → 重新渲染。
虚拟 DOM 是 React 的内存中 UI 表示。当 state/props 变化时,React 将上一次的 UI 描述与新的描述进行比较,然后只对实际变化的部分更新真实 DOM。
避免常见问题的建议:
key 值先从简单做起,再逐步扩展:
优先保证单一事实来源,并通过计算(而非重复存储)来派生其它值,防止不一致。
PageSidebarLayoutStackModalchildrenTextFieldSelectDatePickerItemListItemRow