在 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 中。为了在页面上显示小狗,应用使用静态导入语句在 index.js 中导入 Puppy 组件:

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

如需了解 Next.js 如何打包应用,请在 DevTools 中检查网络轨迹:

  1. 如需预览网站,请按 View App(查看应用)。然后按 Fullscreen(全屏)全屏

  2. 按 `Control+Shift+J`(在 Mac 上为 `Command+Option+J`)打开 DevTools。

  3. 点击网络标签页。

  4. 选中 Disable cache(停用缓存)复选框。

  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 文件的 DevTools 网络,但 index.js 现在小了 0.5 KB。

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

点击该按钮后的 DevTools 网络标签页,其中显示了添加到文件列表底部的额外 1.js 文件和图片。

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

带有自定义加载指示器的动态导入

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

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

如需查看加载指示器的实际效果,请在 DevTools 中模拟网络连接缓慢:

  1. 如需预览网站,请按 View App(查看应用)。然后按 Fullscreen(全屏)全屏

  2. 按 `Control+Shift+J`(在 Mac 上为 `Command+Option+J`)打开 DevTools。

  3. 点击网络标签页。

  4. 选中 Disable cache(停用缓存)复选框。

  5. 节流下拉列表中,选择快速 3G

  6. 点击我按钮。

现在,当您点击该按钮时,系统需要一段时间才能加载该组件,在此期间,应用会显示“正在加载…”消息。

显示“

不使用 SSR 的动态导入

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

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

总结

借助对动态导入的支持,Next.js 可为您提供组件级代码拆分功能,从而最大限度地减少 JavaScript 载荷并缩短应用加载时间。默认情况下,所有组件都是在服务器端呈现的,您可以根据需要停用此选项。