REACT · Vol.VII · LESSON 50 · AI 时代的前端进阶
前沿展望:Server Actions / 边缘计算
Server Actions:一个不写 endpoint 的远程调用
先看一个你写了几十遍的流程:新建书签 = 前端写 fetch 打 /api/bookmarks + 后端写 Controller 接 + 两边对请求体结构。四个字的功能,两端各写一遍「传输层胶水」。Server Actions 的野心就是撕掉这层胶水:
"use server";
import { revalidatePath } from "next/cache";
import { db } from "@/lib/db";
export async function addBookmark(formData: FormData) {
const title = String(formData.get("title") ?? "");
const url = String(formData.get("url") ?? "");
await db.bookmark.create({ data: { title, url } }); // 直接写库,没有中间 HTTP 层
revalidatePath("/bookmarks"); // 让列表缓存失效、重新渲染(第 33 篇失效思路的服务端版)
}
import { addBookmark } from "@/app/actions";
export default function NewBookmarkPage() {
return (
<form action={addBookmark}>
<input name="title" placeholder="标题" />
<input name="url" placeholder="https://example.com" />
<button type="submit">保存</button>
</form>
);
}
没有 fetch、没有 URL、没有手写的 loading/error 分发。原理一句话:'use server' 让编译器把函数变成「只能服务端执行」的安全桩——客户端调用时框架自动生成 POST 端点,参数序列化发过去、服务端执行、结果发回来,再由 revalidatePath 让受影响页面缓存失效。第 42 篇你手工布线的「前端 → /api → 后端」,被折叠成一个函数调用。
你大概率写过 Dubbo 或 OpenFeign:接口在本地、实现在远端,框架负责序列化与网络——「像调本地方法一样调远端方法」。Server Actions 就是 React 官方钦定的前端→服务端 RPC,契约都不用手写:函数签名即契约,框架两端自动生成桩。Next.js 全包的同进程全栈用 Actions 最顺;跨服务集成,该走 HTTP 还走 HTTP。
Partial Prerendering:静态壳秒开,动态洞流式补
第 40 篇见过静态站的速度,第 44 篇见过服务端渲染的灵活,能不能既要又要?Partial Prerendering(PPR,Next.js 15 起以实验特性提供)的答案:把页面拆成「静态壳 + 动态洞」:
声明方式你可能已经猜到——就是第 34 篇的 Suspense 边界:
import { Suspense } from "react";
export default function BookmarksPage() {
return (
<>
<SiteHeader /> // 静态:构建时渲染好,秒开
<Suspense fallback={<ListSkeleton />}>
<BookmarkList /> // 动态洞:请求时流式填充
</Suspense>
<SiteFooter /> // 静态
</>
);
}
静态壳毫秒级到达;动态洞先以骨架占位,数据到了流式补上。第 34 篇你设计的加载态,在这里被框架内建成渲染策略——你声明「哪里会慢」,框架保证「其余全都快」。
你干过「整页走缓存、个别模块 ajax 实时刷新」:页面 90% 缓存五分钟,用户余额那一格单独取。PPR 是同一思想被框架收编——不用自己拆接口、写占位,组件级 Suspense 边界就是「缓存粒度的声明」,失效对应 revalidatePath。
Edge Runtime:把代码部署到离用户 20ms 的地方
第 42 篇的部署模型是「一簇源站服务全国」:上海 10ms,乌鲁木齐 80ms。CDN 解决了静态资源(hash 文件名天生为它而生),动态逻辑仍要回源。Edge Runtime 再进一步:把「执行代码」推到全球数百个边缘节点——渲染、鉴权、个性化在离用户最近的机房完成。
用起来只是多一行声明:
export const runtime = "edge";
export async function GET(request: Request) {
const city = request.headers.get("x-vercel-ip-city") ?? "unknown";
return Response.json({ ok: true, servedFrom: city });
}
能力与代价要一起看。它不是「缩小版 Node」,而是不带标准库的轻量沙箱(V8 isolate):
| 维度 | Node 运行时(第 42 篇的部署方式) | Edge 运行时 |
|---|---|---|
| 冷启动 | 秒级(≈ JVM 冷启动) | 毫秒级,几乎无感 |
| 能力 | 完整 Node API、文件系统、长连接 | 受限沙箱:部分 npm 包与 Node API 不可用 |
| 部署位置 | 你指定的机房 | 全球数百节点,自动就近 |
| 适合 | 重逻辑、数据库长连接、后台任务 | 轻渲染、鉴权、个性化重定向、A/B 分流 |
你见识过同城双活、异地多活:DNS 就近调度、数据同步、机房运维,复杂度翻倍。Edge Runtime 把它打包成平台能力:部署单元从「机器」变成「代码」,push 一次全球生效,节点、调度、扩缩容都不用你管。代价是上表那列沙箱限制——当「CDN 上的轻量函数」用,别当缩小版 JVM。
React Compiler 与 AI:优化和写码的方式都在变
React Compiler:memo 化被编译器接管
第 37 篇你练过 memo、useCallback、useMemo,那是「人肉保证引用稳定」。React Compiler 在构建期分析组件,自动插入等价的缓存代码——同样的优化,从「人肉纪律」变成「机器保证」:
// 第 37 篇的手工优化……
const visible = useMemo(
() => todos.filter((t) => t.title.includes(keyword)),
[todos, keyword]
);
const toggle = useCallback((id: number) => {
setTodos((prev) =>
prev.map((t) => (t.id === id ? { ...t, done: !t.done } : t))
);
}, []);
// 开启 babel-plugin-react-compiler 后,直接写朴素代码:
const visible = todos.filter((t) => t.title.includes(keyword));
const toggle = (id: number) => {
setTodos((prev) =>
prev.map((t) => (t.id === id ? { ...t, done: !t.done } : t))
);
};
前提还是第 37 篇那条规则:引用稳定才跳过重渲染——只是「检查引用是否被篡改」交给了编译器。第 37 篇没有白学:懂「为什么」才看得懂编译器「做了什么」,才能在它对不合规代码放弃优化时(渲染期改 props)手动兜底。JVM 替你管内存,GC 调优工程师依然值钱,同理。
AI:交付单元从「组件」升级到「功能」
第 47 篇你和 AI 结过对,第 48 篇理解了工具调用与状态机。往前看,两条曲线正在合流:
- 自然语言到 UI:一句话生成可用的 React 页面,表单、列表、CRUD 这类「模式化代码」加速最猛——对 AI 这是「分布内」任务,见得多就写得好;
- Agent 生成整个功能:「规划 → 工具调用 → 校验 → 自修复」用于编码 Agent,能跨文件改代码、跑测试、迭代到通过——交付单元从「你写一个组件」变成「你验收一个功能」。
- Server Actions = 官方 RPC:函数签名即契约,胶水由框架生成;鉴权要在函数体内自己做。
- PPR:静态壳 + 动态洞,Suspense 边界就是缓存粒度声明;第 34 篇的加载态被框架内建。
- Edge Runtime:代码即部署单元,全球就近执行;能力受限,当「CDN 上的轻量函数」用。
- React Compiler 接管机械 memo 化;第 37 篇的心智模型是看懂它、兜底它的前提。
- AI 改变「写」的效率;稀缺的是「判断」——契约、架构、归因,恰是本卷反复强调的。
全系列回顾:UI = f(state) 是起点,也是终点
50 篇,7 卷。合上之前,把主线摊在一张表里:
| 卷 | 主线问题 | 关键收获 |
|---|---|---|
| 一 · 核心原语 | 界面为什么这样画? | UI = f(state) 心智模型、JSX、State、diff |
| 二 · Hooks 精讲 | 函数组件怎么「记住」与「响应」? | useEffect、闭包陷阱、自定义 Hook |
| 三 · 状态管理 | 状态放哪、谁说了算? | 客户端/服务端状态分类、Zustand、选型 |
| 四 · 组件工程 | 怎么组织才不会烂? | 组合优于继承、TS、职责边界 |
| 五 · 路由与取数 | SPA 的「后端半边天」怎么补? | Router、TanStack Query、加载/错误/竞态 |
| 六 · 性能与部署 | 怎么又快又稳地送到用户手上? | memo、Profiler、构建、Nginx、CI |
| 七 · AI 时代 | 边界正在怎样消失? | RSC、Server Actions、Agent、边缘计算 |
第 1 篇那句 UI = f(state),走了 50 篇,它长出了三层含义:
- f 在长大:从组件函数长成 RSC(第 44 篇)、Server Actions、边缘函数——「渲染」被推到离数据和用户更近的地方;
- state 在长大:从 useState 到 URL 参数(第 31 篇)、服务端缓存(第 33 篇)——「状态」的边界从内存扩展到整个系统;
- UI 在长大:从 div 到流式 UI(第 45 篇)、AI 生成的界面(第 47 篇)——界面未必全由你手写,但「状态到界面的映射」从未变过。
框架会换代,API 会过时,但「声明式、组件化、状态驱动」这套模型稳定了十年,大概率再稳十年——这是笔记敢以模型为主线的底气。回头看第 43 篇那张转型路线图:基础、生态、工程化、全栈、AI 协作,50 篇下来你把整张图走了一遍。
最后三件事,留给合上专栏之后的你:
- 做项目:把第 49 篇的书签系统扩成简历级产品——加协作、导入导出、PWA。项目是知识唯一的保鲜手段;
- 读源码:从 zustand 这类几百行的小库读实现,到 react.dev 读设计——小库读「怎么写」,大框架读「为什么」;
- 保持在线:订阅 react.dev 与 Next.js 官方博客,盯 RSC、PPR、React Compiler 从实验走向稳定的时间线。
本章回顾 · 全系列收官
回到第 1 篇那个「点击加一」的计数器:那时你抱着一身 Spring 功力,对着一颗手动改 DOM 的心。50 篇之后,你有心智模型、Hooks、状态分层、组件工程、路由取数、性能部署的完整链路,看清了 Server Actions、边缘计算与 AI 重塑的方向,还交付过一个全栈产品。UI = f(state) 是起点也是终点——起点是它完成了你从后端到全栈的心智跃迁,终点是你有资格在更大的尺度上重新定义自己的 f 和 state。江湖路远,带着这套模型继续走。
Comments · 评论