Next.js 中的路由预取

了解 Next.js 如何通过路由预取加速导航,以及如何自定义它。

您将学到什么?

在这篇文章中,您将了解 Next.js 中的路由工作原理、如何针对速度进行优化以及如何自定义它以最好地满足您的需求。

Next.js 中,您无需手动设置路由。Next.js 使用基于文件系统的路由,让您只需在 ./pages/ 目录中创建文件和文件夹

Screenshot of the pages directory containting three files: index.js, margherita.js, and pineapple-pizza.js.

要链接到不同的页面,请使用 <Link> 组件,类似于您使用传统的 <a> 元素的方式

<Link href="/margherita">
  <a>Margherita</a>
</Link>

当您使用 <Link> 组件进行导航时,Next.js 会为您做更多的事情。通常,页面是在您点击链接时下载的,但 Next.js 会自动预取渲染页面所需的 JavaScript。

当您加载包含一些链接的页面时,很可能在您点击链接时,其背后的组件已被预取。这通过加快导航到新页面的速度来提高应用程序的响应能力。

在下面的示例应用中,index.js 页面使用 <Link> 链接到 margherita.js

使用 Chrome DevTools 验证是否预取了 margherita.js:1. 要预览站点,请按查看应用。然后按全屏 全屏

  1. 按 `Control+Shift+J` (在 Mac 上按 `Command+Option+J` )打开 DevTools。
  2. 点击 Network 选项卡。

  3. 选中 Disable cache 复选框。

  4. 重新加载页面。

当您加载 index.js 时,Network 选项卡显示 margherita.js 也被下载了

DevTools Network tab with margherita.js highlighted.

自动预取的工作原理

Next.js 仅预取出现在视口中的链接,并使用 Intersection Observer API 来检测它们。当网络连接速度较慢或用户启用了 Save-Data 时,它也会禁用预取。基于这些检查,Next.js 动态注入 <link rel="preload"> 标签以下载后续导航的组件。

Next.js 仅获取 JavaScript;它不执行它。这样,它不会下载预取页面可能请求的任何其他内容,直到您访问该链接。

避免不必要的预取

为避免下载不必要的内容,您可以通过将 <Link> 上的 prefetch 属性设置为 false 来禁用很少访问页面的预取

<Link href="/pineapple-pizza" prefetch={false}>
  <a>Pineapple pizza</a>
</Link>

在第二个示例应用中,index.js 页面具有一个 <Link>pineapple-pizza.js,其中 prefetch 设置为 false

要检查网络活动,请按照第一个示例中的步骤操作。当您加载 index.js 时,DevTools Network 选项卡显示已下载 margherita.js,但未下载 pineapple-pizza.js

DevTools Network tab with margherita.js highlighted.

使用自定义路由进行预取

<Link> 组件适用于大多数用例,但您也可以构建自己的组件来执行路由。通过 next/router 中提供的路由器 API,Next.js 使这变得容易。如果您想在导航到新路由之前执行某些操作(例如,提交表单),您可以在自定义路由代码中定义它。

当您使用自定义组件进行路由时,您也可以向它们添加预取。要在您的路由代码中实现预取,请使用 useRouter 中的 prefetch 方法。

看看此示例应用中的 components/MyLink.js

预取在 useEffect hook 中完成。如果在 <MyLink> 上的 prefetch 属性设置为 true,则当呈现该 <MyLink> 时,将预取 href 属性中指定的路由

useEffect(() => {
    if (prefetch) router.prefetch(href)
});

当您单击链接时,路由在 handleClick 中完成。一条消息被记录到控制台,并且 push 方法导航到 href 中指定的新路由

const handleClick = e => {
    e.preventDefault();
    console.log("Having fun with Next.js.");
    router.push(href);
};

在此示例应用中,index.js 页面具有一个 <MyLink>margherita.jspineapple-pizza.jsprefetch 属性在 /margherita 上设置为 true,在 /pineapple-pizza 上设置为 false

<MyLink href="/margherita" title="Margherita" prefetch={true} />
<MyLink href="/pineapple-pizza"  title="Pineapple pizza" prefetch={false} />

当您加载 index.js 时,Network 选项卡显示已下载 margherita.js,但未下载 pineapple-pizza.js

DevTools Network tab with margherita.js highlighted.

当您单击任一链接时,Console 会记录“Having fun with Next.js.”并导航到新路由

DevTools Console displaying the message 'Having fun with Next.js.'

结论

当您使用 <Link> 时,Next.js 会自动预取渲染链接页面所需的 JavaScript,这使得导航到新页面更快。如果您正在使用自定义路由,则可以使用 Next.js 路由器 API 自己实现预取。通过禁用很少访问页面的预取来避免不必要地下载内容。