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

如何通过代码拆分和智能加载策略来加快 Next.js 应用的运行速度。

Milica Mihajlija
Milica Mihajlija

发布时间:2019 年 11 月 8 日

了解不同类型的代码拆分以及如何使用动态导入来加快 Next.js 应用的运行速度。

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

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

虽然基于路由的代码拆分是一个不错的默认选项,但您可以通过组件级代码拆分进一步优化加载过程。如果您的应用中有大型组件,最好将其拆分为单独的块。这样一来,任何不重要或仅在特定用户互动(例如点击按钮)时渲染的大型组件都可以延迟加载。

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

动态导入的实际运用

此帖子包含一个示例应用的多个版本,该应用由一个带有按钮的简单网页组成。点击该按钮后,您会看到一只可爱的小狗。在学习应用的每个版本时,您将了解动态导入与静态导入的不同之处,以及如何使用动态导入。

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

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

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

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

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

  3. 点击网络标签页。

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

  5. 重新加载页面。

加载网页时,所有必需的代码(包括 Puppy.js 组件)都捆绑在 index.js 中:

显示六个 JavaScript 文件的开发者工具“网络”标签页:index.js、app.js、webpack.js、main.js、0.js 和 DLL(动态链接库)文件。

当您按 Click me 按钮时,只有对小狗 JPEG 的请求会添加到网络标签页中:

点击按钮后的开发者工具“网络”标签页,显示相同的六个 JavaScript 文件和一个图片。

此方法的缺点是,即使用户不点击按钮来查看小狗,也必须加载 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 组件的代码:

开发者工具“网络”标签页显示了相同的六个 JavaScript 文件,但 index.js 现在小了 0.5 KB。

Puppy 组件现在位于单独的 chunk 1.js 中,只有在您按下按钮时才会加载该 chunk:

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

在实际应用中,组件通常大得多,延迟加载这些组件可以将初始 JavaScript 有效载荷减少数百 KB。

使用自定义加载指示器的动态导入

在延迟加载资源时,最好提供加载指示器,以防出现任何延迟。在 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. 节流下拉列表中,选择快速 3G

  6. 点我按钮。

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

深色屏幕,上面显示了文字

不使用 SSR 的动态导入

如果您只需要在客户端呈现组件(例如聊天微件),可以将 ssr 选项设置为 false

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

总结

Next.js 支持动态导入,可让您实现组件级代码拆分,从而最大限度地减少 JavaScript 负载并缩短应用加载时间。默认情况下,所有组件都通过服务器端渲染,您可以在必要时停用此选项。