← 全栈开发:从入门到入土
全栈开发全栈开发:从入门到入土

React 19 实战:Server Components、Actions,以及手动 memoization 时代的终结

Zero to Depth 系列第 3 篇:React 19 的心智模型、Server Components、Actions 变更模式、1.0 版本的 Compiler,以及 hooks 还剩下什么。

ReactReact 19Server ComponentsServer ActionsReact Compiler前端

React 十三岁了,它的核心思想十三年没变过:UI 是状态的函数。组件是函数,数据向下流,事件向上流。让 React 19(2024 年 12 月)和 19.2(2025 年 10 月)成为 hooks 以来最大变革的,并不是什么新思想——而是这个老思想终于延伸到了整个技术栈。函数有了服务器一侧,表单有了 Server Action,memoization 有了编译器。

本篇讲的是 2026 年年中人们真实使用 React 19 的方式:Server Components 是默认形态,Actions 是变更模式,Compiler 是让你的 useMemo 收藏变成博物馆展品的原因,以及一小撮仍然配得上自己位置的 hooks。

穿越了十年的心智模型

在新机器登场之前,第 2 篇打下的地基依然有效。React 组件就是一个普通的 JavaScript 函数:props 进,UI 描述出。状态是随时间变化的数据;渲染就是 React 拿着当前数据调用你的函数。只要你理解“重新渲染”不过是“函数拿着新输入再跑一遍”,你就理解了 React 的 80%——包括那些大家觉得很神秘的部分。

变化发生在函数在哪里运行。2026 年的默认答案是:在服务器上。客户端是留给例外的地方。

Server Components:边界本身就是特性

React Server Components(RSC)已经稳定,在 Next.js 16 这样的元框架内部,它就是默认的组件类型。需要内化的模型:

  • Server Component 只在服务器上运行——构建时或者按请求运行。它们可以直接读数据库、文件系统和服务器机密。它们给浏览器发送的 JavaScript 数量是。它们不能用状态、effects 或事件处理器,因为它们永远不会在客户端“活过来”。
  • Client Component 是你用 "use client" 标记出来的那些。它们在浏览器里 hydrate,拿到完整的交互工具箱:状态、effects、事件处理器、浏览器 API。

这棵树不是“先服务器、后客户端”两段式的。它是同一棵组件树,边界在其中穿行:一个服务器 Page 可以在服务器 PostList 旁边渲染一个客户端 SearchBar 孤岛,而 PostList 内部又可以嵌一个客户端 LikeButton 孤岛。

从模型推导出来、并且由 linter 强制执行的三条规则:

  1. 客户端孤岛只能从服务器接收可序列化的 props——JSON 形态的数据,加上特殊处理的 Server Actions。不能有类实例、不能有函数、不能有数据库连接。
  2. Server Component 永远不会在客户端重新渲染。数据变了,新的 UI 在服务器上生成,再流式传输过来。
  3. "use client" 是一个边界,而不是整文件的降级——所有被导入进客户端文件的东西都会变成客户端代码,所以要把这个指令往下推到交互真正需要的位置。你发布的 bundle,恰好就是你标记过的那些孤岛。

Actions:变更模型

读数据,靠在服务器上渲染解决;写数据,靠 Actions 解决:RSC 在表单和事件这一侧的对偶,也是替大多数 UI 驱动的变更删掉了样板 API route 的模式。

一个 Server Action 就是一个标记了 "use server" 的函数,客户端可以像调用 RPC 一样调用它——最干净的方式是直接写在 <form action={...}> 里。React 会生成端点、给 action 分配 ID、并向它提交。因为它建立在第 2 篇讲的平台表单语义之上,所以它不需要任何客户端 JavaScript 就能工作——渐进增强是默认行为,不是你额外加的特性。

补全这个模式的 hooks:

  • useActionState —— 把 action 的返回值接回组件里,并白送你一个 isPending。它取代了 useState(isLoading) + useState(error) + try/catch 三件套。
  • useFormStatus —— 让子按钮读到父表单的 pending 状态,提交按钮不需要 prop drilling 就能自动禁用自己。
  • useOptimistic —— 立即展示预期结果(点赞出现、评论发出),服务器应答后自动对账,失败则回滚。这是 2026 年对“感知性能”的回答,也是大多数客户端状态同步的正当替代品。

仍然要守住的纪律:action 是一段通过 HTTP 可达的服务器代码,所以在 action 内部校验输入、检查授权——和任何 API 端点同一套规则。(第 5 篇和第 6 篇会深入讲校验与授权。)

Hooks 还剩下什么

Hooks 没有消失,但它们的岗位描述缩水到了真实大小:

  • useEffect 里取数据的时代结束了。 Server Component 在渲染期间取数;Client Component 用框架的数据层,或者用 use() 消费一个缓存过的 promise。Effects 依然存在——用于与外部系统同步:一个 WebSocket、一个 media query 监听器、一个非 React 的 widget。
  • use()渲染期间读取 promise 和 context,而且和老 hooks 不同,它可以条件调用——“await 这个,但只在这个分支里”终于有了干净答案。
  • useEffectEvent(19.2)终于把“发生在 effect 里的事件”和 effect 的响应式依赖分开了——这是给十年 stale-closure 事故复盘和 ref 绕行方案的正式修复。
  • eslint-plugin-react-hooks v6 在设计上更严格:它会标记破坏 memoization 的写法,实际上等于一次免费的 Compiler 就绪审计。

Compiler:memoization 变成了构建步骤

React Compiler 在 2025 年 10 月发布 1.0,在 Next.js 16 中已经稳定。它在构建时分析你的组件并自动插入 memoization——值缓存、回调稳定性,全套——语义与手写的 useMemo/useCallback 完全一致,除了它永远不会忘记某个依赖。

实际影响:

  • 写组件时就当作每次渲染都很便宜。在常见场景下,编译器会让这件事成真。
  • 手写 useMemo/useCallback 退化成编译器无法证明的少数例外——而且在 review 时值得被追问,就像看到手写 for 循环时你会问为什么不用 map
  • 当某个地方还是觉得慢,答案是测量,不是迷信:React 19.2 给 Chrome DevTools 加了 Performance Tracks——一个展示任务优先级的 Scheduler track,和一个展示渲染耗时与原因的 Components track。

Activity 与预渲染:在后台渲染

19.2 的新增能力都围绕“什么时候渲染”:

  • <Activity> 用声明式边界取代 {isVisible && <Page />}mode="hidden" 让子树保持存活,但卸载 effects、把更新推迟到空闲时段。两个立竿见影的收益——切走再切回来时表单草稿还在的 Tab 界面,以及为用户下一步大概率访问的页面做后台预渲染,让导航感觉瞬时完成。
  • Partial Pre-renderingprerender,然后 resumeresumeAndPrerender)让静态外壳先预渲染、从 CDN 直接发出,动态空洞再按请求恢复。这些 API 主要面向框架作者——Next.js 16 稳定的 PPR 就是这个思想的产品化。
  • 在服务器上,React 现在会把 Suspense 边界的揭示短暂地批量处理,让流式内容以连贯的块出现,而不是一屏瀑布式的转圈——而且 useId 的值现在是合法的 view-transition-name,这种细节会告诉你平台在往哪个方向走(第 2 篇的 View Transitions,来认识一下 React)。

治理与 housekeeping

对长寿项目来说很重要的两条非技术事项:

  • React 有了基金会。 2026 年 2 月,Meta 把 React 捐给了新成立的、隶属 Linux Foundation 的 React Foundation,白金会员包括 Amazon、Callstack、Expo、华为、Meta、Microsoft、Software Mansion 和 Vercel。治理现在厂商中立——对任何准备在这个生态上押注十年的人来说,这是实实在在的去风险。
  • 给你的 React 打补丁。 React2Shell 漏洞(CVE-2025-55182)影响 React 19.0.0 到 19.2.2。如果还有任何部署停留在这些版本上,升级不是可选项。锁定当前版本,并订阅 React 博客的安全动态。

动手练习,然后进入第 4 篇

  1. 搭一个页面:一个从本地数据源(一个 JSON 文件就行)读数据的 Server Component,加一个客户端孤岛——一个计数器。把 "use client" 在树上移上移下,观察框架构建产物里发布的 bundle 如何变化。
  2. 把这个计数器改造成一个带 Server Action 的表单,把值持久化到任何地方(一个文件、SQLite,什么都行)。删掉你的 isLoading 状态,用 useActionState 重写;加上 useOptimistic,看 UI 先于服务器应答。
  3. 在 DevTools 里禁用 JavaScript,然后提交表单。如果还能用,你就是按 2026 年的方式建的。
  4. 开启 React Compiler,打开 Chrome DevTools 的 Performance Tracks。找到一个仍在无谓重渲染的组件——修掉原因,而不是补上 memo。

第 4 篇把镜头拉远一层:把这一切接进路由、缓存和部署的元框架——Next.js 16 对 Astro,以及如何根据你要做的东西的形状来做选择。

guest@swangnice:~$