mcpField
N
next-partial-prefetching-adoption

next-partial-prefetching-adoption

github.com/vercel开发143.2k9.4k 次安装今天更新
代码仓库

关于 next-partial-prefetching-adoption

用于 Next.js 应用迁移到 Partial Prefetching 的 Agent 技能,按路由审查旧预取行为,并验证需要保留的界面。它通过生产模式测试或开发洞察组织迁移步骤。

主要功能

  1. 01审查显式预取链接
  2. 02保留重要的预取界面
  3. 03用 instant() 测试验证迁移
  4. 04借助开发洞察检查路由

适用场景

  1. 01将应用迁移到 Partial Prefetching
  2. 02确认链接点击前可见的界面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/vercel/next.js --skill next-partial-prefetching-adoption

安装后在 Claude Code 中输入/next-partial-prefetching-adoption确认已加载。

附带文件rig-template.md

详细介绍

next-partial-prefetching-adoption

启用 Partial Prefetching,并浏览整个应用,直到每个链接都复用同一个 App Shell。此技能会安排各项工作;针对每项洞察的操作指南位于开发覆盖层修复卡片及其文档页面中。采用 Partial Prefetching 指南是本技能所应用概念的权威参考。

开发洞察与保留测试是两条不同的路径。洞察仅在 next dev 中显示,位于开发覆盖层的 Insights 标签页。由测试验证的保留工作会通过 instant() 对类似生产环境的构建进行测试,无需开发服务器。启用该标志后,单独的 URL 数据洞察检查仍需使用 next dev。

保留门槛

使用由测试验证的保留路径时,第一个实现里程碑是通过关闭标志的 instant() 测试套件。搭建生产测试环境,编写选定的断言,在禁用 partialPrefetching 的情况下运行测试,并记录命令和退出状态。测试环境所需的专用配置(包括 experimental.exposeTestingApiInProductionBuild)是允许的,但在此基准通过之前,不要启用 partialPrefetching,也不要修改目标页面、缓存边界或 Link 属性。安装缺失的测试依赖是达到基准的一部分,不能作为先采用该功能的理由。只有在 rig-template.md 指明仓库无法解决的具体阻碍时,才使用手动路径,并记录该阻碍及延期的测试覆盖范围。

与用户沟通时,谈他们能看到的内容——PR、功能以及应用之后的行为——不要提洞察标识或步骤标签。开始之前,简要说明 Partial Prefetching 会带来哪些变化:指向某个路由的链接会预取一个共享的 App Shell,而 prefetch={true} 还可以解析特定 URL 的缓存内容。审查将确定要保留旧版完整预取中的哪些 UI。

要求

  • 已采用 Cache Components(cacheComponents: true),且构建通过。 partialPrefetching 和路由级 prefetch 导出都要求使用 Cache Components。如果未启用,先使用 next-cache-components-adoption,解决会阻止构建的预渲染错误后再回来。这些错误可能会导致 next build 失败;只有本技能处理的 Partial Prefetching 洞察属于非阻塞的开发信号。

  • Next.js 16.3 或更高版本。 partialPrefetching、prefetch 路由段配置和预取洞察都从此版本开始提供。

  • 可驱动的浏览器。 由测试验证的保留路径使用现有或精简的生产模式 Playwright 测试套件;手动保留和最终演示使用正在运行的生产应用。开发洞察路径和启用标志后的 URL 数据检查需要使用 next-dev-loop;在进行任一开发阶段之前先安装它,除非它已经可用(npx skills add https://github.com/vercel/next.js/tree/canary/skills/next-dev-loop)。如果应用固定使用 webpack,请直接驱动浏览器(agent-browser、Playwright)——你会失去框架交叉检查,但不会失去洞察;它们仍会显示在覆盖层和开发日志中。

  • 可运行的应用。 保留测试和最终演示需要类似生产环境的构建,因为自动预取仅在生产环境中运行。只有使用洞察路径或运行启用标志后的 URL 数据检查时才需要开发服务器;不要仅为了确认由测试验证的保留用例而启动开发服务器。如果应用在导入时会读取数据库或必需的环境变量,请确认所选路径使用的环境能够启动,然后再执行第 1 步。

备注
  • 离线文档。 指南链接在 node_modules/next/dist/docs/ 下有离线副本(自 Next.js 16.2 起随包提供),目录布局带有用于排序的编号(例如 node_modules/next/dist/docs/01-app/02-guides/adopting-partial-prefetching.md)。如果无法预判编号前缀,可使用 find node_modules/next/dist/docs -name '<slug>.md' 查找。/docs/messages/* 错误页面不随包提供。

  • 未附带文档的旧版本。 开始前,先向用户建议运行 npx @next/codemod@latest agents-md:它会将匹配当前版本的副本下载到 .next-docs/,并在 AGENTS.md 中写入索引。此操作会修改仓库中的文件,因此请先征求用户同意,只有用户愿意时才运行。

背景

采用 Partial Prefetching 意味着每条路由都会保留重要的预取 UI;这些 UI 现在分为共享 App Shell,以及链接明确请求的额外逐链接数据。指南是了解预取内容以及如何判断各类情况的权威参考;此技能会针对运行中的应用安排这项工作。

大多数检查工作的关键在于:默认链接只会预热共享 App Shell。由 params 或 searchParams 确定的路由,只有在采用 Partial Prefetching 且特定链接使用 <Link prefetch={true}> 后,才能预取更多内容;随后 Next.js 会在点击之前解析 URL 数据及其背后的任何缓存内容(详见指南的 URL 数据一节)。

工作界面

  • 生产模式的 instant() 测试套件——测试覆盖的保留工作的主要依据。 复用应用的生产构建、测试上下文和 Playwright 设置。先阅读现有的 instant-nav.rig.md;如果项目没有 rig,则根据 rig-template.md 创建。启用前,同一套测试定义旧版目标;每个目标启用部分预取后,这些测试就成为后续工作队列。开发环境可以帮助调查故障,但只有这套测试能判定预取 UI 是否得到保留。
  • 开发服务器终端——洞察路径的主要依据。 每个经过验证的路由,其洞察都会记录为 Error: Route "...": Next.js encountered ... 行,并附带 https://nextjs.org/docs/messages/<slug> 链接。扫描期间持续查看开发日志;它是可通过 grep 搜索的触发位置记录,在 Turbopack 和 webpack 上的工作方式相同。
  • 开发覆盖层的 Insights 标签页。 洞察会显示在琥珀色的非阻塞标签页中。只有触发洞察后才会显示,因此,没有显示任何内容的路由不会出现标签页——这是正常状态,并非功能缺失。不要在没有洞察的路由上寻找标签页;应从上方的开发日志确认状态,因为它是可靠信号。前提是没有阻塞式预渲染错误——这类错误会取代其所在路由上的洞察(参见 requires)。无关的 Issue(例如 hydration 错误、控制台错误)不会妨碍扫描;不要因此停下来。标签页出现时,覆盖层的徽标会显示数量,每条洞察都附有链接到对应文档页面的修复卡片。覆盖层会渲染在 shadow root(nextjs-portal)中,因此无障碍树快照看不到它——如需通过编程方式读取或点击,请对 shadowRoot 求值。
  • 使用 next-dev-loop 执行导航并读取覆盖层。出于与 Cache Components 技能相同的原因,优先使用它,而不是手写浏览器自动化(webpack 应用:参见 requires)。浏览其 /_next/mcp 工具时,预取洞察会通过 get_errors 和覆盖层呈现,而不是名称相似的 get_request_insights。后者是用于记录 span 和性能的工具(由 experimental.requestInsights 控制),不会报告任何预取信息。

每条洞察都有对应的文档页面——请打开它。遇到每种不同的洞察时,都要获取其链接页面;内联消息只是摘要,页面才是具体操作指南。

步骤 1:审查 <Link prefetch={true}> 导航(启用前)

在本次审查和步骤 2 中的旧版基线测试期间,保持全局标志关闭。提前启用会移除迁移需要衡量的旧版行为。如果该标志已在尚未发布的工作中开启,请使用启用标志前的提交进行审查和基线测试。用户有空时,请用 PR 的语言询问其发布方式:

  • 一个分支——在一次更改中完成整个审查,启用标志并在最后运行 codemod(步骤 4)。
  • 逐个路由——每个采用该方案的目标路由都作为独立 PR 发布。尚未处理的目标仍会触发洞察,形成实时工作清单;最后一个目标完成后再执行步骤 4。

无论哪种方式,工作内容和顺序都相同,区别仅在于提交的边界。用户无法联系时,根据应用规模选择默认方式:只有少数链接时使用一个分支;审查规模大到需要让审查者查看较小差异时,逐个路由处理。请在报告中注明选择。

枚举整个源代码树中的显式预取和手动预取位置,不要只检查 app/——它们通常位于 src/components 或共享 UI 包中。从 next/link 的导入和重新导出开始,再追踪自定义封装及其使用位置。使用 rg -n '\bprefetch\b|router\.prefetch' -g '*.tsx' -g '*.jsx' . 作为候选清单,而不是完整审查结果;检查条件属性和转发的 LinkProps,以确定生产环境中的实际值。纳入所有经审查且其生产环境有效 Link 值为 prefetch={true} 的导航:显式 true、不带值的 prefetch 属性,以及结果为 true 的表达式。将默认值、prefetch="auto" 和 prefetch={false} 排除在保留测试集之外,因为它们不会请求旧版完整预取。单独审查现有的 router.prefetch() 调用,因为它们不会触发 Link 洞察。新增手动预取时,请遵循预取指南。如果没有任何 Link 的值解析为 prefetch={true},请说明这一点,并转到步骤 4。

选择要保留的内容及验证方式

在编写测试或编辑目标页面之前,请按照指南中的迁移指南,提出值得保留的 UI。将结果整理成一个简洁的表格:

导航建议结果

合并等效的导航。概述哪些内容会立即就绪,哪些内容会流式传输。如果建议存在歧义,请在运行中的应用中展示该导航,并请用户确认。如果用户无法参与,请遵循指南并记录假设。

确定目标 UI 后,检查现有测试配置。instant() 辅助函数来自单独的 @next/playwright 软件包,而不是 next/experimental/testmode/playwright。

  • 适用的生产模式测试套件: 默认采用由测试支撑的保留方式。复用项目的 @next/playwright 测试、生产脚本、身份验证配置以及现有的 instant-nav.rig.md。遵循指南中的预取 UI 测试工作流,并在采用之前确保关闭标志时的完整测试套件全部通过。不变的断言将驱动迁移,并继续作为回归测试。
  • 没有适用的生产模式测试套件: 使用项目的包管理器和测试惯例,根据 rig-template.md 设置生产模式测试环境。这属于由测试支撑的采用流程,无需用户参与。
  • 测试环境无法可靠运行: 按照 rig-template.md 完成设置和运行状态检查。只有遇到仓库无法解决的具体阻碍(例如凭据不可用或无法访问生产环境)时,才回退到手动保留。记录阻碍和延期的测试覆盖范围;不要声称验证由测试支撑。

复用现有测试套件或创建测试环境都不需要用户输入。只有仓库无法回答环境问题,或目标 UI 本身属于产品决策时,才询问用户。如果用户无法参与,请采用指南中的安全产品默认值,只有遇到具体的测试环境阻碍时才进行手动验证。将新的预取 UI 作为步骤 7 的工作;采用之后,需单独验证任何有意移除的内容。

此工作流针对点击 <Link> 的情况。直接调用 router.prefetch('/dashboard') 属于手动预取,而不是 Link 预取;请在源代码审查中保留这项调用,并在步骤 6 中单独验证。

步骤 2:捕获旧版基线

此步骤中不要启用 partialPrefetching,也不要编辑路由行为、Link 属性或缓存边界。允许使用运行 instant() 所需的仅用于测试的配置。

对于由测试支撑的保留方式,请完成保留门槛:编写完整的 instant() 测试套件,并在关闭 Partial Prefetching 的生产环境模拟测试环境中运行该套件。测试文件、构建、已完成的导航或打印给用户的命令,都不构成基线。只有在测试套件实际通过后,才能继续执行步骤 3。

对于手动保留方式,请在编辑任何目标页面之前,完成现状与目标内容清单。只有通过 rig-template.md 确认存在具体的测试环境阻碍时,才回退到此路径,并记录阻碍和延期的测试。

步骤 3:采用目标位置并还原目标

使用临时路由配置采用每个已审计的目标位置。导出的路由已足够让未更改的测试在该目标位置上验证 Partial Prefetching,同时保持全局标志关闭:

// See: https://nextjs.org/docs/app/guides/adopting-partial-prefetching
export const prefetch = 'partial'

如果其他特定 URL 的 UI 也值得预取,但不属于旧版契约的一部分,请在其链接上保留 prefetch={true},并将该路由标记为步骤 7 的候选项:

// TODO(per-link-prefetch): assess with the user whether URL data should resolve before click.
// See: https://nextjs.org/docs/app/guides/optimizing-prefetching
export const prefetch = 'partial'

使用该确切前缀,以便步骤 7 能通过 grep 找回这些路由。现在不要选择新的目标 UI;只还原从旧行为中选定的目标。

如需通过测试验证保留情况,在每个目标位置更改后,都要重新运行受影响的未更改测试,并将失败项作为后续处理队列。启用全局标志之前,运行完整测试套件并记录其通过时的退出状态。如需手动验证保留情况,请将采用后的生产导航与选定目标进行比较,并记录尚未还原的内容。针对缓存和 Link 属性更改,应用指南中相应的保留模式;如果缓存新鲜度方面的决定不明确,请先询问用户。上面标记的新 URL 数据候选项留待步骤 7 处理。

还原目标会更改缓存或失效处理时,请遵循项目现有的验证方式以及 Revalidating 指南。针对受影响的生命周期,复用或扩展适用的测试套件。如果项目没有测试此类行为,请勿在采用过程中引入新的测试基础设施;应在生产环境中手动验证,并记录预期结果和实际结果。绿色的 instant() 测试只能证明已就绪,不能证明缓存正确性。只有在预期行为不明确时,才询问用户。

如果添加 use cache,请在 next start 下验证,而不只是验证构建。 缓存调用树中的任何位置读取 cookies()/headers()/session,都会在请求时抛出错误,而 next build 可以干净通过。参见 use cache。

步骤 4:启用标志

所有已审计的目标位置都设置了 prefetch = 'partial' 后,分两步完成。

  1. 全局启用该标志。 在 next.config.ts 中设置 partialPrefetching: true(与 cacheComponents: true 一起)。现在所有路由都已采用,因此所有链接都已就绪。

  2. 移除冗余的 prefetch = 'partial' 导出。 使用第一方 remove-partial-prefetch codemod,而不是查找并替换文本。它会移除所有 export const prefetch = 'partial',包括 TODO(per-link-prefetch) 标记下方的导出,并移除自动生成的 Partial Prefetching 指南注释。TODO 标记和其 Optimizing prefetching 指南链接保留,留待步骤 7 处理。其他值(例如 prefetch = 'force-disabled')保留。

    npx @next/codemod@canary remove-partial-prefetch ./app
    

    在使用 src/ 的项目中,请使用 ./src/app,并检查报告的文件数量。工作树不干净时,codemod 会拒绝运行。请先提交或暂存无关改动,或者传入 --force,以便让它的修改与当前 WIP 一起落地。如果 codemod 不可用(@next/codemod 版本较旧、运行环境受限或无法联网),请手动复现其操作:从每个 app/**/{page,layout}.{js,jsx,ts,tsx} 中移除 export const prefetch = 'partial' 及自动生成的 Partial Prefetching 指南注释;保留其他 prefetch 值,并保留原位的 TODO(per-link-prefetch) 标记和 Optimizing prefetching 指南链接。codemod 可以运行时,不要手动编辑。

标志与 codemod 一同落地后,如果采用测试验证路径,请重新运行锁定的保留测试套件。否则,请在最终全局配置下重复已记录的生产环境比较。

步骤 5:检查 URL 数据洞察(启用后)

这是仅供开发使用的第二轮检查。Shell 检查仅在开启标志时运行,会在导航时触发,并且绝不会阻塞构建,因此可在步骤 4 之后的任何时候进行。根据明确的来源(最近一次 next build 的路由表,或 app/ 目录树)创建路由队列,并将其作为待办列表。

按功能逐一检查。一个功能指单个产品界面,例如 app/settings/**、app/posts/[slug]/**,而不是整个顶层区域。开始下一个功能前,先端到端完成当前功能:在 next dev 中加载其路由并处理相应洞察。洞察绝不会阻塞构建,而且每条路由彼此独立,因此部分检查也能留下可正常运行的应用;每项功能都是一项可独立供用户审查或发布的完整改动。

如果环境无法完成整个检查(首次编译较慢、开发服务器在负载下崩溃,或完全没有浏览器),交接前先尽可能完成无需浏览器的工作。对所有可静态处理的路由应用 URL data 中的修复,将读取移至新的 <Suspense> 边界内,并通过类型检查。一次性处理整个队列——更大规模的重构不是推迟处理的理由;询问是否继续检查下一条路由或下一层级也不是检查点;继续推进。只有遇到确实需要判断的情况才停下,并在一份交接报告中集中列出:已静态处理的路由、仍需进行实时 Shell 检查的路由,以及队列。

留意 Insights 选项卡和开发日志中的 Next.js encountered … data 行。本步骤新增的信号是 URL data:在挂起子树中过早读取 params 或 searchParams,会使共享 Shell 与某个 URL 绑定。这类洞察范围较窄;最容易在 generateStaticParams 路由中出现——其中 params 已位于 <Suspense> 下,但仍在 URL 专属的叶节点边界之前被等待。如果出现 blocking-prerender-* 错误,则应用相同的结构性修复。

开启标志后加载路由会预渲染其 App Shell,这会比 Cache Components 构建验证更多路由内容。因此,即使某条路由在 Cache Components 下构建顺利(每条路由均为 ◐,且没有错误),首次预渲染其 Shell 时仍可能出现 blocking-prerender-* 错误——runtime data(cookies()/headers())、uncached data(未缓存的 fetch/DB 调用),或 Date.now()/new Date() 等同步 IO。这并不意味着 Cache Components 迁移尚未完成;这是新的验证,覆盖到了构建过程中从未执行的路径。这些并非 Partial Prefetching 洞察——请像处理其他任何阻塞预渲染错误一样逐一修复。

这些修复很少需要用户参与——每条洞察都会指出问题读取的位置,其文档页面也会说明修复方法,因此直接应用修复并继续检查。将少见的例外留到最后集中询问:整个页面都属于一个依赖 URL 的区域(全部包起来会留下空 Shell),或某条路由可能应继续选择退出。不要用注释叙述重构过程——<Suspense> 边界本身已足够清晰。

第 6 步:验证

与用户确认前的检查清单:

  • Cache Components 采用顺利完成时,扫描结果为空是预期情况。 日志没有提示表示成功,而不是缺少信号。如果你有意探测验证路径,请使用 generateStaticParams 路由,并在 <Suspense> 内、但在 URL 专属叶节点边界之前读取 params;其他结构可能会显示 blocking-prerender-*。
  • App Shell 确实存在:对于你修改的每条路由,请确认导航后的首次渲染显示了预期的共享内容,而不是空 Shell 或卡住的回退内容。用 <Suspense> 包住整个页面主体,即使空 Shell 也能通过验证,这就失去了这样做的意义。
  • 这些洞察验证的是 Shell 的_结构_,而不是预取是否真的发生。请在生产环境运行时确认(自动预取仅在生产环境运行):导航到修改过的链接后,共享 Shell 会立即显示。
  • 对于有测试保障的保留,每个针对已审计 <Link prefetch={true}> 的锁定 instant() 测试都必须在生产环境运行中通过。对于手动保障,必须记录前后清单以及任何延后处理的测试事项。
  • 填充任何数据可更新的新缓存或扩展缓存后,必须通过适用的现有测试套件或记录的手动检查来验证下一次读取时的变更情况;如果项目没有这类覆盖,就进行手动检查并记录。
  • 如果应用以命令式方式预取,洞察扫描不会覆盖它,因此扫描结果为空并不能证明预取在启用标志后仍然有效。请在 next start 下验证该调用:比较启用前后的 _rsc 预取响应或资源计时,并确保任何有意保留的完整预取仍会携带旧调用预热的数据。如果现在只返回 App Shell,请使用与最近的 <Link prefetch={true}> 目标相同的决策来迁移该调用点——缓存数据,或将每个链接的预取行为迁移到文档支持的 <Link prefetch={true}>。
  • 在将路由故障归咎于该标志之前,请先在关闭 partialPrefetching 的情况下复现问题(或在启用标志之前的分支上复现)。该标志会更早、更明显地暴露已有问题——脆弱的请求时身份验证门控、重写、部署版本不一致——但很少是问题的起因。如果关闭标志时也会出错,那就不是 Partial Prefetching 问题;应在那里修复,而不是在这里。
  • next build 仍然可以通过。

然后与用户确认。使用用户的语言,不要提洞察 slug 或步骤标签。

  • 你做了什么:审计了哪些链接、采用了哪些目标,以及每个链接现在会预取什么。
  • 改了什么:移除了哪些属性、添加了哪些 use cache 边界,以及哪些路由带有待后续处理的 TODO(per-link-prefetch) 标记。
  • 在生产环境运行中演示。自动预取仅在生产环境运行,因此 next dev 无法展示结果——请运行 next build 和 next start,并把运行地址交给用户。运行时需要应用的真实环境(数据库、身份验证、密钥);不完整或过时的安装,或遗留的生成产物,都可能因与采用无关的原因导致构建失败。请事先说明,验证需要完整且具备凭据的生产环境运行,并非快速检查。
  • 展示,而不是只说:在有头浏览器中直接操作一个链接,连接到生产服务器,让用户看到共享 App Shell 立即渲染,URL 专属区域随后流入。只有无法使用实时浏览器时,才附上前后截图。
  • 提供点击验证路径:列出每条修改后的路由、要点击的链接,以及点击后的预期效果(哪些内容立即渲染,哪些内容会流入),让用户可以自行验证每项结果。
  • 要问的问题:“在我们查看哪些路由也应该预取 URL 专属内容之前,要先提交这些改动(或打开 PR)吗?”等待用户回答——采用和每链接预取最好作为各自独立的改动。

步骤 7:按链接预取(可选)

审核标记了超出已保留的旧版约定的候选项,而没有替你做决定。搜索 TODO(per-link-prefetch),并在一次对话中与用户逐项讨论。对于每个路由,都要询问用户:是否希望在点击前预取依赖 URL 的额外内容,还是导航后再流式加载即可。每个可预取链接都会产生一次服务端调用——指南中的权衡部分可作为检查清单。不要独自做出这些决定。

如果答案是否定的,删除标记,让该路由使用 App Shell 默认设置。如果答案是肯定的,请遵循预取优化指南,通过生产环境运行确认选择启用的链接,并在验证所选结果后删除标记。

完成此步骤后,不得残留任何 TODO(per-link-prefetch) 标记。按链接进行的优化应与采用工作分开提交,或放在单独的 PR 中。

最后,用简洁的 Navigation | Why it may no longer be needed 表格列出所有实际生效的 prefetch={false} 链接。说明 false 会禁用所有预取,而部分预取的默认 auto 行为只会预取共享的 App Shell,因此,为避免旧版全路由预取而添加的退出设置现在可能已无必要。请用户之后单独重新审视这些设置。

延伸阅读

评论

支持 Markdown