Next.js 链接预取(Prefetching)实战指南:基于 with-prefetching 示例掌握默认、命令式与禁用三种预取模式
2026/9/8 23:46:53 网站建设 项目流程

Next.js 链接预取(Prefetching)实战指南:基于 with-prefetching 示例掌握默认、命令式与禁用三种预取模式

【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js

本指南以当前仓库中的 with-prefetching 示例 为主体,系统讲解 Next.js 路由预取(Prefetching)机制的三种典型用法:<Link>默认的视口自动预取、通过router.prefetch()触发的命令式预取,以及用prefetch={false}精确禁用预取。读完本文,你将能在真实页面中按需组合这三种策略,理解预取背后的源码触发条件,并为页面/应用路由两种路由形态的导航体验做出合理的取舍。

示例总览:四种页面,三种预取策略

with-prefetching 是一个极简的 pages router 应用,通过Nav导航组件把三种预取策略直观地"演示"在同一个导航栏里,每种策略对应一到两个页面:

  • Home / Features:走 Next.js 默认预取行为——只要<Link>出现在视口内(viewport)就自动在后台预取目标页面;
  • About:命令式(Imperative)预取——通过prefetch={false}先关闭自动预取,再借助router.prefetch("/about")onMouseEnter等自定义时机手动拉取;
  • Contact:完全禁用预取——<Link prefetch={false}>之后,即使用户悬停也不发起预取。

示例的导航组件源码位于 components/Nav.tsx,四个页面(pages/index.tsx、pages/features.tsx、pages/about.tsx、pages/contact.tsx)都只是渲染标题占位,用于在浏览器 Network 面板中直观对照"有没有预取请求"。页面本身通过 _app.tsx 统一挂载导航组件:

import type { AppProps } from "next/app"; import Nav from "../components/Nav"; export default function App({ Component, pageProps }: AppProps) { return ( <> <Nav /> <Component {...pageProps} /> </> ); }

示例依赖非常精简(见 package.json):nextreactreact-dom,配合dev/build/start三个标准脚本,方便直接聚焦预取行为本身。

模式一:默认 API —— Link 进入视口即自动预取

示例导航栏的默认策略对应下面这段 JSX:

<Link href="/">Home</Link> <Link href="/features">Features</Link>

在默认配置下,Next.js 的<Link>会自动对出现在视口内的链接发起预取。从 packages/next/src/client/link.tsx 的源码注释可以确认它的语义:

"Any<Link />that is in the viewport (initially or through scroll) will be prefetched."(任何处于视口内——无论是初始还是滚动进入——的<Link />都会被预取。)

也就是说,Home 与 Features 两个链接一旦出现在用户可视区域内,浏览器就会在后台悄悄下载对应路由的产物与数据。等到用户真正点击跳转时,目标页面资源已就绪,导航因此显得"瞬时完成"。

源码层确认:预取触发的实际条件

在 link.tsx 中,组件会先根据 prop 计算预取开关:

const prefetchEnabled = prefetchProp !== false

随后通过useIntersection之类的可见性检测拿到isVisible,并把"可见"与"开关开启"两个条件同时满足作为发起预取的前提(见 link.tsx):

// If we don't need to prefetch the URL, don't do prefetch. if (!isVisible || !prefetchEnabled) { // ... } prefetch(router, href, as, { ... });

其中prefetch内部会调用router.prefetch(href, as, options)(见 link.tsx),并对每个 URL 建立去重集合,避免重复拉取同一目标(见 link.tsx)。预取请求即使失败也不会阻断后续真实导航——它本身只是"提前做好功课"的优化手段。

值得注意的一个细节:同一源码注释与逻辑也写明,视口自动预取只在 production 构建下生效;在开发模式下<Link>仅会在 hover 时预取,以避免浪费资源(开发时预取会触发页面即时编译,见 link.tsx 附近注释)。因此验证"自动预取"效果时,应使用next build && next start而非next dev

模式二:命令式 API —— 在 onMouseEnter 等事件中手动预取

自动预取对"立刻可见"的链接很理想,但当预取成本较高,或你希望把预取时机从"进入视口"改为"用户即将点击"等更精确的时刻时,就需要命令式预取。示例中 About 链接的写法如下:

<Link prefetch={false} href="/about" onMouseEnter={() => { router.prefetch("/about"); console.log("prefetching /about!"); }} > About </Link>

这里有两层关键设置:

  1. prefetch={false}关闭自动预取:先取消<Link>的默认行为,避免同一目标被重复拉取;
  2. onMouseEnter中调用router.prefetch("/about"):鼠标刚进入链接区域(即用户表现出导航意图)时,通过next/routeruseRouter()拿到的router实例手动发起预取。

这与 Next.js 官方建议的"hover 即预取"思路一致:从鼠标移入链接到真正点击,通常有几百毫秒的间隔,恰好可以把预取时间藏进这段"用户思考时间"里。示例在回调里打印日志,方便你在控制台直观看到命令式预取确实被触发了:

prefetching /about!

命令式 API 的意义在于完全自定义触发时机。除了onMouseEnter,还可以把它接到onFocusonTouchStart、定时器、滚动阈值,甚至某个业务事件上。凡是代码能拿到router实例的地方,理论上都能主动调度一次预取。

提示:命令式预取的场景同样适合其他导航事件驱动型 UI。若你的导航是在自定义组件中实现而非使用<Link>router.prefetch几乎是实现"接近默认体验"预取效果的标准手段。

模式三:禁用 API —— 用 prefetch={false} 精确关停

并不是所有链接都值得预取。以下场景你可能希望彻底关掉预取:

  • 目标页面路由的产物很大,预取会挤占当前页所需带宽;
  • 目标属于低频入口,预取命中率低、得不偿失;
  • 目标页面需要大量动态数据,预取数据过期快,价值有限。

示例中 Contact 链接给出了最简写法:

<Link prefetch={false} href="/contact"> Contact </Link>

仅仅加一个prefetch={false}布尔属性即可。在 link.tsx 中它会使prefetchEnabled变为false,进而让上文if (!isVisible || !prefetchEnabled)这条分支无论链接是否可见都直接短路——既不自动预取,也不会在 hover 时预取。

从 link.tsx 的文档注释还能看到,在 app router 场景下prefetch属性还支持"auto"true两种取值:

  • "auto"/null/undefined(默认):静态生成页面会预取完整的 React Server Component 数据;动态页面则只预取到最近的带有loading.js的路由段,避免拉取过多数据;
  • true:无论是否存在loading.js分段,都预取所有路由段的完整数据;
  • false:任何情况下都不预取数据(包括 hover)。

不过需要注意,本示例本身跑在 pages router(使用next/routeruseRouter),prefetch布尔开关与"仅 production 生效"的语义在两个路由形态下都成立;而'auto'/true这类精细的数据粒度控制,是 app router<Link>的能力。选用哪种取值,应以你项目实际采用的路由形态为准。

如何运行与使用本示例

本地引导

使用create-next-app可以直接从示例模板初始化项目。仓库 README 提供三种主流包管理器对应的命令:

npx create-next-app --example with-prefetching with-prefetching-app
yarn create next-app --example with-prefetching with-prefetching-app
pnpm create next-app --example with-prefetching with-prefetching-app

执行后会在当前目录生成with-prefetching-app应用。也可以直接在本仓库的examples/with-prefetching目录内安装并启动:

# 在 examples/with-prefetching 下 pnpm install pnpm dev # 开发模式 pnpm build # 生产构建 pnpm start # 运行生产服务器

观察三种预取差异的验证步骤

由于视口自动预取只在 production 生效,建议按以下流程验证:

  1. 依次执行pnpm buildpnpm start,打开首页;
  2. 打开浏览器开发者工具 Network 面板;
  3. 停留在首页不滚动、不悬停,观察Home/Features链接对应的路由资源是否已在进入视口时被拉取(默认 API);
  4. 将鼠标移入 About 链接但不点击,观察控制台输出prefetching /about!且 Network 中出现/about资源(命令式 API);
  5. 悬停或滚动到 Contact 链接,确认始终没有针对/contact的预取请求(禁用 API)。

云端部署

示例的 README 也提供了标准的 Vercel 部署入口:可通过create-next-app拉取后直接导入 Vercel 一键部署,也可在 Vercel 控制台以该示例仓库为源创建新项目(FRAMEWORK选择 Next.js 即可自动识别)。部署形态与本地next start一致,预取行为不会因运行环境不同而改变。

生产实践建议

结合示例与源码,可以总结出几条落地准则:

  1. 把默认自动预取当"默认项":绝大多数站内导航都应保留默认行为,让视口可见的链接自动预取,换取最低成本的体验提升;
  2. 命令式预取用于"有明确交互信号"的入口:自定义导航组件、需要比视口更早/更晚触发预取的场景,使用router.prefetch+prefetch={false}组合,把触发时机收回到业务代码手中;
  3. 用禁用预取管理成本:对超大体量或低命中率的目标路由显式prefetch={false}
  4. 区分运行环境验证:开发模式下只有 hover 才会预取,务必在next build && next start后的 production 环境中核对真实预取流量;
  5. 重复预取是无害且被去重的:内部以 URL 维度维护去重集合(见 link.tsx),因此即便自动预取与命令式预取针对同一链接同时存在,也不会产生双重请求。

将本示例的Nav.tsx对照你项目中的导航组件,逐条核对上面三种策略各自的命中场景,即可系统性地把预取优化落到真实页面中。

<输出文章>

【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询