Next.js 中的路由预提取

Next.js 如何通过路线预提取功能加快导航速度,以及如何对其进行自定义。

米利卡·米哈吉利亚 (Milica Mihajlija)
Milica Mihajlija

您将学到什么?

在这篇博文中,您将了解 Next.js 中的路由的工作原理、如何针对速度进行优化,以及如何根据自己的需求对其进行自定义。

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

包含以下三个文件的网页目录的屏幕截图:index.js、margherita.js 和 pineapple-pizza.js。

如需链接到不同页面,请使用 <Link> 组件,方法与使用旧版 <a> 元素类似:

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

当您使用 <Link> 组件进行导航时,Next.js 会为您做一些工作。通常,当您点击指向相应网页的链接时,该网页就会下载,但 Next.js 会自动预提取呈现网页所需的 JavaScript。

当您加载包含几个链接的网页时,可能在您点击某个链接时,系统已经提取了该链接背后的组件。这样可以更快地导航到新页面,从而提高应用响应速度。

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

使用 Chrome 开发者工具验证是否已预提取 margherita.js: 1. 如需预览网站,请按查看应用,然后按全屏 全屏

  1. 按 `Control+Shift+J`(在 Mac 上,则按 `Command+Option+J`)打开开发者工具。
  2. 点击网络标签页。

  3. 选中停用缓存复选框。

  4. 重新加载页面。

加载 index.js 时,Network 标签页显示 margherita.js 也已下载:

突出显示 margherita.js 的开发者工具“Network”标签页。

自动预提取的运作方式

Next.js 仅预提取视口中显示的链接,并使用 Intersection Observer API 进行检测。当网络连接缓慢或用户开启了 Save-Data 时,它还会停用预提取。根据这些检查,Next.js 会动态注入 <link rel="preload"> 标记,为后续导航下载组件。

Next.js 只会提取 JavaScript,而不会执行 JavaScript。这样,在您访问链接之前,它不会下载预提取的网页可能请求的任何其他内容。

避免不必要的预提取

为避免下载不必要的内容,您可以将 <Link> 上的 prefetch 属性设置为 false,从而针对很少访问的网页停用预提取功能:

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

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

如需检查网络 activity,请按照第一个示例中的步骤操作。加载 index.js 时,开发者工具 Network 标签页会显示 margherita.js 已下载,但 pineapple-pizza.js 并未下载:

突出显示 margherita.js 的开发者工具“Network”标签页。

使用自定义路由预提取

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

使用自定义组件进行路由时,也可以为其添加预提取功能。如需在路由代码中实现预提取,请使用 useRouter 中的 prefetch 方法。

以此示例应用中的 components/MyLink.js 为例:

预提取在 useEffect 钩子内完成。如果 <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 页面包含一个指向 margherita.jspineapple-pizza.js<MyLink>prefetch 属性设置为 /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 并未下载:

突出显示 margherita.js 的开发者工具“Network”标签页。

点击任一链接后,控制台都会记录“Happy with Next.js.”(享受 Next.js 的乐趣)并导航到新路由:

开发者工具控制台显示“玩转 Next.js 得心应手”消息。

总结

当您使用 <Link> 时,Next.js 会自动预提取呈现链接网页所需的 JavaScript,以便更快地导航到新网页。如果您使用的是自定义路由,则可以使用 Next.js 路由器 API 自行实现预提取。通过针对很少访问的网页停用预提取功能,避免不必要地下载内容。