在 Next.js 中使用动态导入功能进行代码拆分

如何利用代码拆分和智能加载策略加快 Next.js 应用的速度。

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

您将学到什么?

这篇博文介绍了不同类型的代码拆分以及如何使用动态导入功能来加快 Next.js 应用的速度。

基于路由和基于组件的代码拆分

默认情况下,Next.js 会针对每个路由将您的 JavaScript 拆分为单独的代码块。当用户加载您的应用时,Next.js 仅发送初始路由所需的代码。当用户在应用中导航时,他们会提取与其他路由关联的分块。基于路由的代码拆分可最大限度地减少需要同时解析和编译的脚本量,从而缩短网页加载时间。

虽然基于路由的代码拆分是一个很好的默认选项,但您可以通过在组件级别进行代码拆分进一步优化加载过程。如果您的应用中包含大型组件,最好将其拆分为多个单独的代码块。这样一来,任何不重要或仅在某些用户互动(例如点击按钮)时呈现的大型组件便可被延迟加载。

Next.js 支持动态 import(),可让您以动态方式导入 JavaScript 模块(包括 React 组件),并将每个导入内容作为单独的分块加载。这样,您就可以进行组件级代码拆分,并控制资源加载情况,以便用户仅下载他们正在查看的网站部分所需的代码。在 Next.js 中,这些组件默认采用服务器端渲染 (SSR)

动态导入的实际运用

这篇博文介绍了一个示例应用的多个版本,该应用由一个带有一个按钮的简单页面组成。点击该按钮后,您会看到一只可爱的小狗。在浏览应用的每个版本时,您会看到动态导入与静态导入有何不同,以及如何使用动态导入。

在应用的第一个版本中,小狗生活在 components/Puppy.js 中。为了在页面上显示小狗,应用会使用静态 import 语句在 index.js 中导入 Puppy 组件:

import Puppy from "../components/Puppy";

如需了解 Next.js 如何捆绑应用,请检查开发者工具中的网络跟踪记录:

  1. 如需预览网站,请按查看应用,然后按全屏 全屏

  2. 按 `Control+Shift+J`(在 Mac 上,则按 `Command+Option+J`)打开开发者工具。

  3. 点击网络标签页。

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

  5. 重新加载页面。

当您加载页面时,所有必要的代码(包括 Puppy.js 组件)都会捆绑在 index.js 中:

“开发者工具 Network”标签页,其中显示了以下六个 JavaScript 文件:index.js、app.js、webpack.js、main.js、0.js 和 dll(动态链接库)文件。

当您按 Click me 按钮时,系统仅会将小狗 JPEG 的请求添加到 Network 标签页中:

点击按钮后的 DevTools Network 标签页,同时显示 6 个 JavaScript 文件和 1 个图像。

这种方法的缺点是,即使用户不点击按钮才能看到小狗,他们也必须加载 Puppy 组件,因为它包含在 index.js 中。在这个小示例中,没有什么大不了的,但在实际应用中,仅在必要时加载大型组件通常是一个巨大的改进。

现在查看应用的第二个版本,其中静态导入替换为动态导入。Next.js 包含 next/dynamic,这让您可以对 Next 中的任何组件使用动态导入:

import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";

// ...

const Puppy = dynamic(import("../components/Puppy"));

按照第一个示例中的步骤检查网络跟踪记录。

当您首次加载应用时,只会下载 index.js。这次,该文件小 0.5 KB(从 37.9 KB 缩减到 37.4 KB),因为它不包含 Puppy 组件的代码:

显示相同的 6 个 JavaScript 文件的开发者工具网络,只不过 index.js 现在缩减了 0.5 KB。

Puppy 组件现在位于一个单独的块 1.js 中,该块仅在您按下按钮时加载:

点击按钮后的“开发者工具 Network”标签页,显示额外的 1.js 文件和添加到文件列表底部的图片。

在实际应用中,组件通常要大得多,因此延迟加载组件可将初始 JavaScript 载荷减少数百千字节。

包含自定义加载指示器的动态导入

延迟加载资源时,最好提供加载指示器,以防出现任何延迟。在 Next.js 中,您可以通过向 dynamic() 函数提供一个额外的参数来实现此目的:

const Puppy = dynamic(() => import("../components/Puppy"), {
  loading: () => <p>Loading...</p>
});

如需查看加载指示器的实际效果,请在开发者工具中模拟慢速网络连接:

  1. 如需预览网站,请按查看应用,然后按全屏 全屏

  2. 按 `Control+Shift+J`(在 Mac 上,则按 `Command+Option+J`)打开开发者工具。

  3. 点击网络标签页。

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

  5. Throttling 下拉列表中,选择 Fast 3G

  6. 点击我按钮。

现在,当您点击该按钮时,需要一些时间来加载组件,同时应用会显示“正在加载...”消息。

深色屏幕(包含文字)

不使用 SSR 的动态导入

如果您只需要在客户端渲染组件(例如聊天 widget),可以通过将 ssr 选项设置为 false 来实现:

const Puppy = dynamic(() => import("../components/Puppy"), {
  ssr: false,
});

总结

Next.js 支持动态导入,因此提供组件级代码拆分,可最大限度减少 JavaScript 载荷并缩短应用加载时间。默认情况下,所有组件都在服务器端渲染,您可以根据需要停用此选项。