在 Next.js 应用中开启缓存组件,并解决由此暴露的阻塞路由。当用户想要启用、采用或迁移到缓存组件、翻转 `cacheComponents` 标志、处理大量 blocking-prerender / instant 验证错误、运行 `cache-components-instant-false` codemod,或决定是使用 `export const instant = false` 将路由排除在验证之外还是原地修复时使用。
next-cache-components-adoption
在应用中启用缓存组件,并使其构建通过。本技能按顺序执行工作;每个错误的修复方法位于开发覆盖层的修复卡片和构建的终端输出中。迁移到缓存组件指南 是本技能应用的概念和每个 API 修复方法的权威参考——当技能步骤提到某个模式("use cache"、cacheLife、<Suspense> 放置等)时,请查阅它以获取完整解释。
要求
-
App Router 项目。 缓存组件是 App Router 的特性;
cacheComponents: true对pages/路由无效。如果项目有pages/或src/pages/目录但没有app/或src/app/目录,请停止并告知用户——Pages 到 App 的迁移是另一个项目,不属于本技能范围。混合应用(同时有pages/和app/)是可以的:该标志影响app/路由;pages/路由不受影响,也不需要退出。 -
可运行的应用。 整个循环需要针对
next dev和浏览器进行验证,因此应用必须能启动。如果它在导入时读取数据库或必需的环境变量(例如,一个env.ts在缺少DATABASE_URL时抛出异常),请确认它确实能启动——使用真实环境或你搭建的本地数据——然后再进行步骤 1。无法运行的应用无法验证采用过程。 -
Next.js 16.3 或更高版本。 该版本包含了本技能依赖的功能:顶层
cacheComponents、export const instant、开发覆盖层的即时导航验证警告,以及cache-components-instant-falsecodemod。如果next --version显示版本低于 16.3,请先升级: -
没有不兼容的配置键。
cacheComponents: true会在任何仍然导出dynamic、revalidate或fetchCache的文件上报错。转换,不要删除。 每个导出都编码了路由需要保持的行为;通过迁移指南中每个键的章节将其迁移到对应的缓存组件等价物。例外情况是dynamic = 'force-dynamic':在缓存组件下,每个路由默认已经是动态的,因此迁移指南直接删除它而不是转换——不要对一堆相同的force-dynamic删除过度思考。revalidate和fetchCache仍然需要真正的转换。如果某个值无法干净地转换,请留下// TODO: Cache Components adoption — restore revalidate = 3600注释,以便循环处理。cache-components-instant-falsecodemod 不会处理这些。 -
experimental.dynamicIO是致命的。 它已被重命名为顶层cacheComponents,旧键现在会在任何构建运行前中止——请先删除它(或替换为cacheComponents: true)。experimental.useCache仍然作为已弃用的别名被接受;一旦设置了cacheComponents: true,它就是多余的,因此为了清晰起见请删除它。
注意
-
在启用标志之前没有通过的基线。 如果应用已经使用了
"use cache",启用标志前的构建会报错please enable the feature flag cacheComponents。启用标志是你做的第一件事(在增量模式中,在 codemod 之前;在直接模式中,在修复路由之前)——而不是在获得通过的构建之后才做的事情。在你的开始总结中注意这一点,以免它看起来像是回归。 -
离线文档。 指南链接有离线副本,位于
node_modules/next/dist/docs/(自 Next.js 16.2 起捆绑),目录结构按顺序编号(例如node_modules/next/dist/docs/01-app/02-guides/migrating-to-cache-components.md)。如果你无法预测编号前缀,使用find node_modules/next/dist/docs -name '<slug>.md'来解析。/docs/messages/*错误页面没有捆绑。 -
没有捆绑文档的旧版本。 建议用户在开始前运行
npx @next/codemod@latest agents-md:它会下载一个版本匹配的副本到.next-docs/并将索引写入AGENTS.md/CLAUDE.md。这会修改他们仓库中的文件,所以先询问,仅在他们同意时运行。
工作形态
只有一个循环:自上而下遍历路由树,一次一个特性,针对 next dev + 浏览器采用每个路由。构建是每个特性的最终检查,而不是工作表面。
步骤 1 中的选择是:是先将所有路由排除在验证之外,还是边修复边进行。无论哪种方式,循环都是相同的:
- 带有安静预步骤(增量模式)。 运行 codemod 将每个页面和布局排除在验证之外。一旦你也修复了 codemod 无法处理的问题(同步 IO 调用、剩余的
revalidate/dynamic/fetchCache导出),构建通过;你将其作为单独的 PR 提交,然后开始循环——一次移除一个退出声明并采用该路由。这可以将工作拆分成小的、可审查的 PR。 - 不带预步骤(直接模式)。 启用
cacheComponents并从构建首先标记的任何路由开始循环。相同的循环,但所有修复都在一个分支上,直到采用完成。
在两种模式中,每个路由的成功标准是相同的:开发循环报告没有错误,并且 next build 通过。在每个特性之后与用户确认,并建议提交,但未经他们确认绝不提交。预计大部分时间花在循环中,而不是预步骤中。
背景
cacheComponents: true 要求每个路由都是可预渲染的。在 <Suspense> 外部读取请求时数据的路由是“阻塞”的,会导致构建失败。export const instant = false 将路由标记为允许阻塞,这会在开发模式和构建中清除它;在布局上,它会在构建期间覆盖整个子树,但客户端导航仍然会单独验证每个后代段。包裹在 "use cache" 函数中的读取被视为缓存边界,而不是阻塞读取。
三类阻塞器通常按此顺序出现:
- 请求时读取(
cookies()、headers()、await params、await searchParams)。当在页面或布局顶部等待时,这四个都会阻塞。params和searchParams经常被忽略,因为它们不像 cookies 和 headers 那样被视作“请求数据”。修复方法是将读取推入一个<Suspense>包裹的子组件中——对于params/searchParams,将 promise 转发到子组件并在那里等待;不要在页面顶部await。 - 模块/渲染时的同步 IO(
new Date()、Date.now()、Math.random()、crypto.randomUUID())。即使有instant = false,这些也会导致构建失败——退出声明不会抑制它们。如果它们位于共享布局中,它们会阻塞其下的每个路由。codemod 无法修复它们;你必须手动转换每一个,构建才能通过(参见增量预步骤)。在运行其他任何操作之前,在整个仓库中搜索这些调用。 - 读取请求数据的
"use cache"文件。 带有顶层"use cache"指令的文件不能导出instant;将两者结合会报错Only async functions are allowed to be exported in a "use cache" file.,这意味着该路由的指令是错误的。在运行 codemod 之前删除它。
工作表面
查找阻塞路由
在工作时优先使用 next dev 而不是 next build。
next dev——工作表面。访问一个路由;其阻塞错误会出现在开发覆盖层中,带有完整的堆栈跟踪和链接到每个错误文档的修复卡片。一次处理一个路由——错误不会累积在一个地方。路由本身仍然返回 HTTP 200,所以读取覆盖层(或.next-dev.log),而不是状态码。清除覆盖层是调用路由干净的一半——另一半是浏览器验证(参见步骤 2)和该路由的通过构建。next build——仅检测。构建是next dev的权威检查,而不是替代品。将其用作循环中每个特性的最后关卡(通过的构建是每个路由成功标准的一部分),并作为整个应用的最终验证。在增量模式中,构建还确认预步骤(codemod 将每个路由排除在验证之外,没有共享布局仍然有同步 IO 阻塞器)在你提交该 PR 之前。在处理路由时,不要用构建代替开发循环——通过编译并不能告诉你哪些内容进入了静态外壳,哪些流式传输了。默认情况下,构建在第一个阻塞路由处停止,因此它也不适合评估工作量。两个标志在迭代时有所帮助:--debug-build-paths只构建你命名的路由(相对于项目根目录的文件路径的逗号分隔的 glob 模式,例如--debug-build-paths="app/admin/**/page.tsx"——不是 URL 路径;--debug-build-paths="app/(marketing)/about/page.tsx"——不是/about;--debug-build-paths="app/admin"不匹配任何内容并静默构建零个路由),而--debug-prerender禁用提前退出,因此构建会继续通过第一个预渲染失败,报告每个阻塞路由,并打印更完整的堆栈跟踪,指明原始文件和行号。
每个阻塞错误都有一个文档页面——打开它。开发覆盖层和构建终端都会打印一个 https://nextjs.org/docs/messages/<slug> 链接,每个错误都有。该页面是修复的权威方法;内联消息是摘要。为你遇到的每个不同错误获取该链接,即使你认为自己知道模式——这些方法会演变,并且相同的错误类可能根据路由读取的内容而有不同的正确修复。不要仅凭内联消息即兴发挥。(/docs/messages/* 页面没有离线捆绑;如果你没有网络,请回退到 node_modules/next/dist/docs/ 下的每个 API 指南,并在报告时注明限制。)
在运行时验证每个修复
通过的构建或清除的覆盖层并不能证明路由实际行为正确——缓存组件是一个运行时问题(带有流式数据的静态外壳)。在每次修复后验证,而不仅仅是在最后。
按偏好顺序:
-
next-dev-loop——强烈推荐。 通过agent-browser交叉检查/_next/mcp与实时浏览器,并在一次通过中暴露编译和运行时问题。诊断信息(React 树、Suspense 边界、控制台 + 网络)比手动检查next dev更丰富。在开始循环之前安装它。不要等到遇到
next dev单独无法解释的问题。运行:npx skills add https://github.com/vercel/next.js/tree/canary/skills/next-dev-loop该技能会说明其所需的
agent-browser版本并引导你完成安装。需要 Turbopack。 如果
package.json中的dev脚本传递了--webpack,请向用户指出并询问是否有理由继续使用 webpack。如果没有,切换到 Turbopack(Next.js 16.3+ 的默认值)。如果他们想保留 webpack,则跳过此安装并使用仅构建循环。你不需要权限来安装
next-dev-loop本身。它是一个工具,就像安装开发依赖一样。如果用户在场,简要告知他们你正在安装它用于验证。在非交互式运行(CI、仪表板、沙箱)中,无需询问即可安装——“无法提示用户”不是跳过的理由。唯一合理的跳过是真正的技术障碍:没有网络、没有 npm、只读文件系统、明确的无新依赖策略,或仅 webpack 的开发脚本。如果你跳过,请在最终报告中说明具体的障碍。 -
你可以自己驱动的浏览器。 Playwright、直接使用
agent-browser、任何浏览器自动化工具。仅在next-dev-loop确实被阻塞时使用。你会错过框架端的检查(/_next/mcp),因此仅靠 DOM 断言无法捕获所有回归——在称其为“已验证”时要更加谨慎。 -
仅构建。 如果你根本无法运行开发服务器,那么构建是你唯一的信号。
○ (Static)路由没有<Suspense>,完全由构建验证(没有流式内容需要测试)。◐ (Partial Prerender)路由仅验证了外壳——在报告时标记它们。 -
没有任何工具。 要求用户运行开发服务器(或构建)并报告他们看到的内容,或者移交你已经达到的里程碑。
步骤 1:选择策略
以用户想要的 PR 形式询问,而不是工作量大小。与用户交谈时绝不使用内部标签(增量、直接)——这些是你自己的脚手架。以 PR 和特性来询问,例如:"您希望我先打开一个 PR 来开启缓存组件并将每个路由排除在验证之外,然后在后续 PR 中逐个特性处理实际的路由采用吗?还是在一个分支上完成所有工作?" 即使在一个很小的应用上,增量路径仍然有价值(可审查大小的 PR、可回滚、// TODO: Cache Components adoption 标记同时作为你下次会话的工作队列)。不要替他们做选择。
如果没有用户可询问,默认选择增量模式并记录该选择。
- 增量——安静的预步骤 + 循环。运行 codemod 将每个页面和布局排除在验证之外,使构建通过,停下来与用户确认(参见预步骤结束),然后进入步骤 2 的循环并将每个特性作为后续 PR 提交。
- 直接——跳过预步骤。启用
cacheComponents并直接进入步骤 2 的循环;构建的阻塞路由就是工作队列。
增量
在调用 codemod 之前,修复它无法处理的两类阻塞器。
-
模块/渲染时的同步 IO。 在整个仓库中搜索
new Date()、Date.now()、Math.random()和crypto.randomUUID()(不仅仅是app/**/layout.{js,jsx,ts,tsx}——读取可能存在于布局导入的任何组件中)。使用其blocking-prerender-*错误卡片中的await connection()+<Suspense>修复来解除每个匹配的阻塞:它将值推迟到请求时,与迁移前完全相同,因此不需要产品决策。在await connection()上方添加以下确切注释:// TODO: Cache Components adoption. Added to unblock the build: remove this connection() to re-trigger the error and review the fix options.它与 codemod 写入的注释共享
TODO: Cache Components adoption前缀,因此确认时的 grep 可以找到两者。删除await connection()会使错误再次触发并显示其修复卡片——与在循环中移除退出声明相同的操作。 -
不兼容的段配置。 在
app/中搜索^export const (revalidate|dynamic|fetchCache)并根据上面的requires注释进行转换。codemod 不会处理它们;保留它们会导致 codemod 后构建失败。
codemod 拒绝在脏工作树上运行。先提交或暂存不相关的工作,或者传递 --force 让它的编辑与你的 WIP 共存。常见的误报:如果你最近升级了 Next.js,package.json 和锁文件可能已经是脏的——先提交它们。
使用 @canary 频道,而不是 @latest。cache-components-instant-false 转换不在稳定的 @next/codemod 版本中;@next/codemod@latest 会报错 Invalid transform choice。
npx @next/codemod@canary cache-components-instant-false ./app
将 export const instant = false(带有 // TODO: Cache Components adoption 注释)插入到每个 app/**/{page,layout,default} 文件中,跳过已经声明 instant 的文件以及任何标记为 "use client" 或 "use server" 的模块。然后设置 cacheComponents: true。TODO 注释是循环的工作队列。
如果 codemod 不可用(较旧的 @next/codemod、沙箱环境、离线运行),请手动重现:对于每个不是 "use client" 或 "use server" 且尚未声明 instant 的 app/**/{page,layout,default}.{js,jsx,ts,tsx} 文件,在导入之后插入:
// TODO: Cache Components adoption. Refactor this route so this opt-out can be removed.
// See: https://nextjs.org/docs/app/guides/migrating-to-cache-components
export const instant = false
codemod 有意将每个段都排除在外,而不仅仅是根段。解析是自上而下的,第一个显式配置获胜:最高的 instant = false 决定整个子树。如果每个段都有退出声明,移除一个段的退出声明只验证该段;后代保留自己的退出声明并保持通过。如果只有根段被排除在外,移除它将重新启用整个应用的验证。
因为最高的退出声明获胜,所以自上而下地移除它们(先根布局,然后向下)。当祖先仍然持有退出声明时,移除叶子的退出声明没有任何作用。
使用 next build 确认预步骤。构建是证明,而不是 codemod 运行——一个调用 new Date() / Math.random() 的共享布局无论退出声明如何仍然会失败(参见背景)。
构建通过后,确认根布局获得了退出声明(grep -n "export const instant" app/layout.*)。根布局渲染每个路由,包括框架路由如 /_not-found,因此如果它被遗漏了,请手动向它添加 export const instant = false。
像 /_not-found 这样的合成路由没有用户文件——当它们阻塞时,修复根布局的退出声明,而不是合成路由。客户端组件("use client")不会获得退出声明(从它们导出 instant 是构建错误——E1344),但它们不是罕见的阻塞器。高频情况是根布局的导航或标题中的客户端组件调用 usePathname()/useSearchParams():它会阻塞_每个_动态路由,并显示 blocking-prerender-client-hook,而静态路由通过(路径名在预渲染时已知),这掩盖了它,直到你到达一个动态段。这不是祖先数据修复——请遵循错误的文档页面中的 <Suspense> 方法。只有当客户端路由在_服务器_数据上阻塞时,你才在其祖先中修复该数据。
预步骤结束:确认
仅增量模式。在开始步骤 2 之前在此处停止——预步骤是可提交的 PR。用用户的语言与他们交谈;不要说“增量”或其他内部标签;谈论采用、PR 以及应用现在做什么。告诉他们:
- 你做了什么:开启了缓存组件,运行了 codemod 将每个页面和布局排除在新的验证之外(或手动完成),修复了 codemod 无法处理的任何阻塞器(列出它们),确认构建通过。
- 发生了什么变化:
app/中的每个页面和布局现在都导出了instant = false并带有// TODO: Cache Components adoption注释,除了客户端组件和任何已经有instant导出的组件。 - 要检查什么:差异主要是机械性的(新的导出 + 注释)。构建通过。路由的行为仍然与之前完全相同——退出声明保留了当前行为;还没有渲染变化。
- 问题:“您想将其作为单独的 PR 提交,然后再开始逐个路由采用缓存组件吗?还是继续在这个分支上?”等待回答。
不确认就进入步骤 2 会破坏采取增量路径的意义。
直接
设置 cacheComponents: true 并进入步骤 2。构建的阻塞路由就是工作队列。
步骤 2:内部循环,一次移除一个特性的退出声明
一个“特性”是一个单一的产品表面——app/settings/profile/**、app/posts/[slug]/**——而不是像 app/dashboard/** 这样的整个顶层应用。在开始下一个之前完成一个端到端。
在一个特性内,自上而下进行(布局在页面之前,根布局优先)。在移除后代之前移除布局的退出声明会暴露布局自身的阻塞读取。(直接模式:没有退出声明可移除——修复每个失败的路由;如果手写的退出声明在祖先上遮蔽了它,先移除那个。)
中途通过的构建并不意味着布局是干净的。在移除布局的退出声明而其后代页面仍然有它们自己的退出声明时,构建保持通过——每个页面遮蔽了继承的验证。布局的实际阻塞读取只有在它下面没有任何东西遮蔽时才会暴露。不要在布局边界就声称特性完成。
使用带浏览器的循环,除非浏览器确实不可达。next-dev-loop 技能是“浏览器可用”以及如何安装它的权威来源。
带浏览器的循环(推荐)
每个路由:
- 移除退出声明(增量模式)或定位失败的路由(直接模式)。
- 在开发模式下重新加载。覆盖层干净?跳到验证。覆盖层仍然红色?修复。
- 修复——从错误链接的文档页面(
https://nextjs.org/docs/messages/<slug>)获取修复方法,应用那里的方法。内联覆盖层文本是摘要;文档页面是权威来源。 - 在浏览器中验证。确认首次绘制时的可见内容是你在外壳中预期的——而不是卡在回退上,也不是静默地将所有内容从空外壳中流式传输出来。
- 如果修复涉及共享代码(布局、侧边栏组件),重新检查兄弟路由。共享外壳的更改可以修复你正在处理的路由,但可能破坏兄弟路由。
仅构建循环(回退)
当无法驱动浏览器时使用——CI、沙箱、用户没有运行 next dev 且你无法启动一个。信号较弱:确认构建通过且路由预渲染,但无法确定静态外壳与流式内容中分别有什么。
每个路由:
- 移除退出声明(增量模式)或定位失败的路由(直接模式)。
- 使用
--debug-build-paths app/<route>/**(仅该路由)或--debug-prerender(完整构建,但通过第一个失败)重新构建。路由通过?继续。仍然阻塞?修复。 - 修复——从错误链接的文档页面(
https://nextjs.org/docs/messages/<slug>)获取修复方法,应用那里的方法。 - 如果修复涉及共享代码,重新检查兄弟路由。
- 在移交特性时将该路由标记为仅构建验证。每个
◐路由在特性完成之前仍然需要浏览器通过。
循环注意事项
- 背景中的三类阻塞器 在就地修复时经常被忽略。缓存下游获取(
getThing(id))并不能清除页面主体顶部的await params——将参数 promise 推入<Suspense>包裹的子组件中。 - 模糊的调用是用户确认点,而不是代理判断。当你不确定哪种修复适合、阻塞代码看起来涉及安全、或者用户可能希望故意保持路由阻塞时——在编辑之前阅读 references/per-page-decisions.md。在询问时展示路由:
next-dev-loop会话以有头模式运行浏览器,因此导航到页面并将其留在屏幕上,以便用户看到他们正在决定的内容,当有头浏览器不可用时,截图作为回退。“这个应该保持阻塞吗?”在看着页面时比看着文件路径容易回答得多。 - 不要用注释叙述重构。codemod(或你)应该留下的唯一注释是退出声明上的
// TODO: Cache Components adoption,以及用户现有的注释。不要为每个<Suspense>边界或"use cache"调用添加注释说明其作用——代码已经说明了。仅在代码中_为什么_不明确时添加注释(例如,带有原因的故意阻塞)。
保持该特性路由的待办列表。当特性中的每个路由都干净时,进入步骤 3。
步骤 3:验证特性
在向用户确认之前的检查清单:
next build完成且没有阻塞路由错误。- 特性中没有裸露的 TODO:
grep -rn "TODO: Cache Components adoption"会找到 codemod 的退出声明注释和预步骤中的同步 IO 解除。任何剩余的instant = false都是故意的、有文档的阻塞——注释已重写为原因(参见 references/per-page-decisions.md → “何时保留阻塞”)。任何剩余的await connection()都经过审查并有意保留,而不是预步骤遗留的。 - 在浏览器中访问每个路由:确认静态外壳首先渲染,并且每个
<Suspense>回退都解析为其真实内容。如果可能,捕获两种状态——回退(流中)和最终绘制——以便你可以向用户展示流式体验演示。如果流式传输太快无法观察,在浏览器中限制网络。
然后向用户确认。与预步骤相同的规则:用他们的语言说话。不要说“逐个特性循环”或其他内部标签;谈论你采用的特性以及用户将看到什么。
- 你做了什么:你接触了哪些路由,以及每个路由的用户可见结果(例如,“文章页面现在在骨架屏后面流式传输文章主体,而布局保持静态”)。
- 发生了什么变化:移除了退出声明,添加了回退,引入了缓存边界。
- 展示,而不是讲述。
next-dev-loop会话以有头模式运行浏览器,因此实时驱动路由,让用户看到静态外壳 → 回退 → 最终内容的序列。如果你无法驱动实时浏览器,请附上你捕获的前后截图。 - 给他们点击路径:该特性路由的简短表格——要打开的 URL 以及要查看的内容(哪些内容立即渲染,哪些回退出现,哪些流式传输进来)——以便他们可以自己验证每个路由。
- 问题:“您想将此特性作为 PR 提交并继续下一个,还是在此处停止?”等待回答。
琐碎的特性可以跳过确认。 如果采用一个特性只意味着移除其 // TODO: Cache Components adoption 退出声明(没有添加 <Suspense>,没有引入 'use cache',没有渲染顺序变化),用户看不到任何不同。继续下一个特性而不停止;在下次确认时顺便提及。
当循环在所有特性上运行完毕——每个剩余的 instant = false 都在原因注释下,grep -rln "TODO: Cache Components adoption" app 返回空——将用户指向进一步阅读,如果他们想进一步推动体验,或者停止并提交。
路由表符号
ƒ → ◐ 是采用通常达到的状态。◐ (Partial Prerender) 意味着静态外壳预渲染,请求时内容流式传输进来——对于任何读取 cookies()、headers()、params 或 searchParams 的路由来说,这是目标状态。某些路由通过有文档的逃生舱口(例如,使用 await connection() 的布局)进行请求时工作,因此它们合法地保持 ƒ;该页面不再是_退出_的,而是真正的动态。不要为了追求 ◐ 而移除逃生舱口。反之亦然:instant = false 不会强制路由为 ƒ。该符号反映了路由在预渲染时做什么,而不是它导出了哪些验证旋钮。
◐ 告诉你存在一个外壳,而不是外壳里有什么。放置得太高的 <Suspense> 边界(例如,包裹整个页面主体,或文章内容周围的 <Suspense fallback={null}>)会将可见内容推出静态外壳进入流式负载;构建仍然报告 ◐,因为_某些_外壳预渲染了(通常只有 <html><body> 和框架标记)。路由表无法告诉你外壳里有什么;浏览器可以。如果外壳是空的并且所有内容都流式传输,将 <Suspense> 边界向下拉近到实际的动态读取。
进一步阅读
以下工作是可选的,位于文档中——将用户链接到它们,让他们决定接下来做什么。不要在本技能内部引导这些工作。
- 扫描更多即时导航——采用完成后可选的后续工作,从不要求。通过的构建不是最终结论,因为开发模式会在每次页面加载时验证每个路由(模拟页面加载和客户端导航),并捕获构建的首次错误退出和后代替换所跳过的内容。将其作为对不想采用部分预取的用户实现即时导航的更小路径提供。采用部分预取(下文)运行相同类型的循环并同样满足这些见解,因此推荐两者,让用户选择哪个或是否进行。该参考是要执行的循环。
next-partial-prefetching-adoption——采用部分预取的后续技能:它启用partialPrefetching并根据决策表审计每个<Link prefetch={true}>(或使用关闭的标志增量采用,由link-prefetch-partial见解驱动)。它像本技能对缓存组件所做的那样对工作进行排序,但见解仅限开发模式,因此是浏览器点击通过,而不是构建循环。在即时导航之后推荐,因为这些修复直接影响到外壳可以预取每个路由的多少内容。概念位于采用部分预取指南中。- 使用 e2e 测试防止回归——
@next/playwright的instant()辅助函数断言导航后立即可用的 UI,因此回归会在 CI 中暴露。一旦路由是即时的,推荐使用:next-dev-loop确认它_现在_是即时的;instant()测试保持它如此。 next-cache-components-optimizer——一个单独的技能,用于增加每个路由的静态外壳,使更多页面预渲染,更少内容流式传输。纯优化,不是采用的一部分。






