一句”帮我审审 Next.js 代码”会漏掉真正拖垮生产的 App Router 陷阱:一个 useState 污染了服务端组件、一个本该裹进 use cache 的 fetch、一个改完数据却没调 updateTag 的 Server Action。到了 Next.js 16(2025 年 10 月 21 日起稳定,截至 2026 年 6 月 npm 稳定版为 16.2.7),默认行为又变了:缓存改成显式 opt-in、middleware.ts 正被 proxy.ts 取代、Turbopack 成为默认打包器、cookies() / params 变成异步。下面 15 个 Prompt 各盯一个 App Router 特有的失败模式,已按 Next.js 16 模型刷新。每个 Prompt 都把确切的 next 版本贴进去:模型通常比框架慢好几个月,回答只跟你给的版本一样新。
一句话总结
- 先跑 Prompt 1(边界审计)——边界 bug 会级联到后面每一条发现。
- Next.js 16 把缓存改成显式 opt-in:
use cache+cacheLife()+cacheTag()取代旧的隐式 fetch 缓存。Prompt 2、4、5、13 都按新模型重写了。 middleware.ts→proxy.ts(Node runtime)、cookies()/headers()/params异步化,分别在 Prompt 9、10 里查。- 每个 Prompt 都要求
file:line证据、并禁止 AI 重写代码,输出始终可 review。 - 每次都把
next.config.ts和确切的next版本贴进 Prompt——模型的回答只跟你给的版本一样新。
适合哪些场景
正在发 App Router 应用的工程师(Next.js 13.4 到 16)、审 Server Action PR 的 reviewer、从 Pages Router 迁移的独立开发者、版本升级后排 hydration / 缓存意外的团队。
Next.js 16 改了什么(审查前先看)
如果你审的是刚升级过的代码库,下面这些默认行为都变了。每个缓存类 Prompt 都要先确认代码库用的是哪套模型。
| 方面 | Next.js 13–15 | Next.js 16(截至 2026 年 6 月) |
|---|---|---|
| 缓存 | 隐式;13/14 默认缓存 fetch,15 默认不缓存 | 显式 use cache 指令 + cacheLife() / cacheTag()(Cache Components) |
| 默认渲染 | 不用动态 API 就是静态 | 部分预渲染(PPR)——静态外壳 + 流式动态空洞 |
| Edge/Node 拦截层 | middleware.ts | proxy.ts(Node runtime);middleware.ts 已弃用,仅保留给 Edge |
| 缓存失效 | revalidateTag(tag) | updateTag(tag)(read-your-writes,仅 action)和 revalidateTag(tag, profile) |
cookies() / headers() / params / searchParams | 同步(15 起改异步) | 异步——必须 await |
| 打包 / lint | 默认 webpack;next lint | 默认 Turbopack;next lint 已移除(直接跑 ESLint/Biome) |
Pages Router 代码跳过这套(缓存、数据获取、组件模型都不一样),纯静态营销站也跳过(根本不碰 App Router 的服务端特性)。最适合的场景:App Router 迁移 PR、上线前的缓存 / revalidate 审计、Server Action 安全审查、hydration / 边界排错,以及 Next.js 升级后的性能回归排查。
15 个可直接复制的 Prompt 模板
1. server vs client 边界审计
第一个跑——边界 bug 会级联。
You are a senior Next.js reviewer. Audit this App Router code for SERVER vs CLIENT boundary violations: (1) files with `"use client"` that should be server (no interactivity), (2) server files importing client-only APIs (window, useState), (3) client components receiving non-serializable props, (4) deep client trees that could be shifted to server. For each: file:line, why it's wrong, the fix. Do not refactor.
2. Server Action 正确性 review
Review every `"use server"` action in this code for: (1) input validation (zod / valibot / manual), (2) auth check at the TOP of the action — server actions are publicly callable endpoints, (3) cache invalidation after mutation: `updateTag` (read-your-writes, preferred in Next.js 16), `revalidateTag(tag, profile)` with its required cacheLife profile, or `revalidatePath`, (4) error handling that returns a typed result object (not throw), (5) idempotency for retries. Output: action name | finding | severity.
3. route handler review
Review `route.ts` handlers in `app/api/`: (1) Method handlers (GET/POST/etc) — any missing? (2) Response shape consistency, (3) `dynamic = "force-dynamic"` vs default — is the choice intentional? (4) Auth middleware coverage, (5) CORS for cross-origin. List findings with file:line.
4. fetch 缓存 / use cache 审计
Next.js 16 把”缓存模式”这一问换成 use cache 覆盖率。
This is a Next.js 16 codebase using Cache Components (`cacheComponents: true`). Audit data fetching in server components and route handlers. For each fetch/db call: (1) Is it inside a `use cache` function/component, wrapped in `<Suspense>`, or neither (which throws "Uncached data was accessed outside of <Suspense>")? (2) Does cached data have a `cacheLife()` profile matching its freshness needs and a `cacheTag()` for targeted invalidation? (3) Any user-specific data inside `use cache` without the user key in scope (cross-user leak)? (4) Duplicate fetches per request that should be deduped. File:line evidence required. If the repo is on Next 13/14/15, review classic `fetch` cache modes (default / no-store / force-cache / revalidate=N) instead.
5. 缓存失效覆盖
updateTag / revalidateTag / revalidatePath / refresh 行为各不相同——把每个写操作映射到对的那个。
Map every mutation (server action, POST route handler, webhook). For each: (1) Does it invalidate the cache it changed? (2) Is the API correct for the intent — `updateTag` for read-your-writes (user sees their own edit immediately, actions only), `revalidateTag(tag, profile)` for stale-while-revalidate content, `revalidatePath` for whole-path, `refresh` for uncached live data? (3) Does the tag match a `cacheTag()` actually set on the read side? (4) Reads that won't reflect the mutation because they aren't tagged? Note: in Next.js 16 single-arg `revalidateTag(tag)` is deprecated and needs a cacheLife profile. List gaps as: mutation | should invalidate | currently invalidates.
6. streaming 与 Suspense 审计
Review use of Suspense boundaries and streaming in this App Router code (Next.js 16 uses Partial Prerendering by default, so anything not in `use cache` and not wrapped in `<Suspense>` blocks the static shell). Identify: (1) uncached/runtime data not wrapped in Suspense, (2) loading.tsx files missing where they'd help LCP, (3) waterfalls of awaited fetches that should be parallelized, (4) Suspense boundaries placed too high (whole-page loading) or too low (jittery), (5) an empty Suspense fallback above <body> in a root layout that accidentally opts the whole app out of the static shell. Suggest specific Suspense placements.
7. metadata 与 SEO review
Audit Next.js metadata (`generateMetadata` + static metadata): (1) every route has a title and description? (2) Open Graph and Twitter card present for shareable routes? (3) `alternates.canonical` set for routes reachable at multiple URLs? (4) Dynamic metadata using the same data as the page (no duplicate fetch)? File:line.
8. <Image> 与资源优化 review
Review `<Image>` usage in this code: (1) `width` and `height` explicit (no layout shift)? (2) `priority` set on above-fold images? (3) `sizes` attribute correct for responsive use? (4) `remotePatterns` in next.config covers all sources? (5) Any `<img>` tags that should be `<Image>`? Output: image | issue.
9. middleware / proxy.ts review
Next.js 16 把 middleware.ts 改名为 proxy.ts(Node runtime);把漏改的揪出来。
Review the request interceptor (`proxy.ts` in Next.js 16, or legacy `middleware.ts`): (1) On Next.js 16, is it still named `middleware.ts` and exporting `middleware()` when it should be `proxy.ts` exporting `proxy()`? (2) matcher config covers the intended routes only — no overmatching that runs on every asset? (3) Auth checks early-return correctly? (4) Header / cookie mutations applied via `NextResponse.next({ headers })` rather than mutating the request? (5) Any heavy business logic that belongs in a route handler or server action, not the interceptor? File:line findings.
10. cookies 与 session review
Audit cookie usage across server components, server actions, and route handlers: (1) On Next.js 15+ is every `cookies()` / `headers()` call `await`ed (they are async)? (2) Reads via `cookies()` outside a `<Suspense>` boundary that block the static shell when the surrounding content could be cached? (3) httpOnly / secure / sameSite flags correct? (4) Session reads duplicated per request that should be deduped with React `cache()` or pulled out and passed into a `use cache` component as a prop? (5) Cookies set in server components (illegal — must be in actions / route handlers)?
11. error / not-found 边界 review
Review `error.tsx`, `global-error.tsx`, `not-found.tsx`: (1) Every route segment with possible errors has one? (2) Error boundaries log to observability? (3) `reset()` functions are wired up? (4) `notFound()` called from data-fetch paths that 404? List missing files and missing handlers.
12. 并行 / 拦截路由 review
If this app uses parallel routes (`@slot`) or intercepting routes (`(.)folder`), audit: (1) Default slots present where required? (2) Slot loading / error files in place? (3) Intercepting routes degrade correctly when accessed directly via URL? If no parallel/intercepting routes exist, say so — do not invent.
13. dynamic / static 渲染审计
For each route segment, determine rendering mode. On Next.js 16 with Cache Components, classify as: fully prerendered static shell / PPR (static shell + streamed dynamic holes) / fully dynamic. Output a table: route | mode | what forces request-time work (cookies()/headers()/searchParams/connection()/uncached fetch) | could it be moved into `use cache` instead? Flag routes that defer the whole shell to request time (e.g. an empty Suspense fallback over <body>) — those are usually perf regressions. On Next 13/14/15, classify as static / dynamic / ISR instead.
14. App Router 升级兼容 review
升级 Next.js minor/major 后用。
I just upgraded Next.js from `[fromVersion]` to `[toVersion]`. Review this code for breakage and new-best-practice opportunities specific to that jump. If upgrading to 16, specifically flag: sync `cookies()`/`headers()`/`params`/`searchParams` that must now be awaited; `middleware.ts` that should become `proxy.ts`; implicit fetch caching that now needs `use cache` + `cacheLife`/`cacheTag`; single-arg `revalidateTag(tag)` missing its cacheLife profile; `next lint` scripts (removed); and any `runtime = 'edge'` defaults. Output: file | change | severity (breaking / warning / nice-to-have).
可替换变量: 两个版本占位符——例如 15.4 和 16.2。
15. App Router 发现 → 修复 PR 计划
最后跑——把发现转成 PR 序列。
Take the findings from previous App Router reviews and group them into 3-5 PRs sized for solo review (each < 400 LOC diff). For each PR: title, files touched, dependency on other PRs, rollback plan. Output as markdown.
容易踩的坑
- 当 Pages Router 审——缓存、数据获取、组件模型都不同。
- 用 Next 14 的认知审 Next.js 16 仓库——缓存现在是显式的(
use cache),不再隐式。先确认版本。 - 写操作后没做缓存失效——read-your-writes 用
updateTag,能容忍短暂滞后的内容用revalidateTag(tag, profile)。典型症状就是生产读到旧数据。 - 全部
"use client"——服务端组件的好处全没了,要审多余的 client 边界。 - 看到
dynamic = "force-dynamic"就放过——多半藏着缓存配错。 - 不查
proxy.ts/middleware.tsmatcher——overmatch 会在每个静态资源上都跑一遍,拖垮性能。 - Server Action 顶部不加 auth 门——它是公开可调用的端点。
- 忘了 Next.js 15+ 的
cookies()/headers()/params是异步的——没 await 的 Promise 当真值用,会埋下静默 bug。
优化技巧
- 永远先跑 server/client 边界(Prompt 1),其他发现都源自这里。
- 每个 Prompt 都把确切的
next版本和next.config.ts(好让模型知道有没有开cacheComponents)一起贴进去。模型的回答只跟你给的版本一样新。 - 每条发现都要
file:line证据和触发这个问题的具体 import 或 directive。 - 缓存审计按路由跑,别全局跑——不同路由缓存预期不同。
- Server Action 必须显式 review auth、validation、失效。
- 每次 Next.js 升级跑升级兼容 Prompt(14)——能抓默认值变化(缓存、异步 API、
proxy.ts)。 - 把发现做成 checklist,每个修复 PR 后重跑——缓存 bug 爱回来。
- 人工 review 配合
next build(现在会报 Suspense 外的未缓存数据)和next startLCP 对比——Prompt 抓不到真实运行时回归。
FAQ
- 这套也适用于 Pages Router 吗?: 不适用——模型完全不同。Pages Router 代码用通用的 React 审查 Prompt。
- 代码库升到了 Next.js 16 但还在用旧缓存写法,怎么审?: 这是最常见的升级缺口。先跑 Prompt 14 把隐式 fetch 缓存找出来,再用 Prompt 4 把每个 fetch 映射到
use cache+cacheLife/cacheTag。同时告诉模型next.config.ts里有没有设cacheComponents: true。 updateTag和revalidateTag——Server Action 该用哪个?:updateTag(tag)给的是 read-your-writes(用户立刻看到自己刚改的内容,会阻塞到数据刷新为止),只能在 Server Action 里用。在 Route Handler 或 webhook 里改用revalidateTag(tag, profile)——它是 stale-while-revalidate,适合能容忍短暂滞后的内容。到了 Next.js 16,单参数的revalidateTag(tag)已弃用,必须带一个 cacheLife profile(内置名如'hours'/'days'/'max',或next.config.ts里自定义的)。- Server Action 调了很多 helper,怎么界定审查范围?: 粘 Server Action 文件加上它直接调用的那几个 helper,别粘整个仓库——聚焦比覆盖更重要。
- AI 知道最新的 Next.js 版本吗?: 只有你告诉它才知道。在 Prompt 里写明确切版本;对刚发布的版本,把相关的 changelog 或升级指南片段也贴一段。模型通常比框架慢好几个月。
- 15 个能一次跑完吗?: 能,但发现会糊成一团。先跑边界,再跑缓存,最后其他。
- Edge runtime 怎么办?: 在 Prompt 里加一句:“Some routes run on Edge — flag Node-only APIs in Edge-runtime files.” 注意 Next.js 16 里 Node-runtime 的拦截层是
proxy.ts;middleware.ts仅保留给 Edge,但已弃用。
相关阅读
外部参考:Next.js 16 发布说明 和 Cache Components 缓存指南 是 use cache 模型的权威来源;审查刚升级过的代码时,把相关章节贴进 Prompt 里。