Skip to content

软件包打包

¥Package Bundling

打包是将应用代码及其依赖合并为针对客户端和服务器优化的输出文件的过程。更小的包加载速度更快,可以减少 JavaScript 执行时间,提升 核心网络生命力 的性能,并缩短服务器冷启动时间。

¥Bundling is the process of combining your application code and its dependencies into optimized output files for the client and server. Smaller bundles load faster, reduce JavaScript execution time, improve Core Web Vitals, and lower server cold start times.

Next.js 通过代码分割、摇树优化和其他技术自动优化包。但是,在某些情况下,你可能需要手动优化你的包。

¥Next.js automatically optimizes bundles by code splitting, tree-shaking, and other techniques. However, there are some cases where you may need to optimize your bundles manually.

有两种工具可用于分析应用的包:

¥There are two tools for analyzing your application's bundles:

本指南将引导你了解如何使用每种工具以及如何生成 优化大型包

¥This guide will walk you through how to use each tool and how to optimize large bundles.

Next.js 包分析器(实验性)

¥Next.js Bundle Analyzer (Experimental)

适用于 v16.1 及更高版本。你可以在 专用 GitHub 讨论 中分享反馈,并在 turbopack-bundle-analyzer-demo.vercel.sh 中查看演示。

¥Available in v16.1 and later. You can share feedback in the dedicated GitHub discussion and view the demo at turbopack-bundle-analyzer-demo.vercel.sh.

Next.js 包分析器已集成到 Turbopack 的模块图中。你可以使用精确的导入跟踪来检查服务器和客户端模块,从而更容易找到大型依赖。打开交互式 Bundle Analyzer 演示以浏览模块图。

¥The Next.js Bundle Analyzer is integrated with Turbopack's module graph. You can inspect server and client modules with precise import tracing, making it easier to find large dependencies. Open the interactive Bundle Analyzer demo to explore the module graph.

步骤 1:运行 Turbopack 打包分析器

¥Step 1: Run the Turbopack Bundle Analyzer

要开始使用,请运行以下命令并在浏览器中打开交互式视图。

¥To get started, run the following command and open up the interactive view in your browser.

bash
npx next experimental-analyze

步骤 2:模块筛选和检查

¥Step 2: Filter and inspect modules

在用户界面中,你可以按路由、环境(客户端或服务器)和类型(JavaScript、CSS、JSON)进行筛选,或按文件搜索:

¥Within the UI, you can filter by route, environment (client or server), and type (JavaScript, CSS, JSON), or search by file:

¥

步骤 3:使用导入链追踪模块

¥Step 3: Trace modules with import chains

树状图将每个模块显示为一个矩形。模块的大小由矩形的面积表示。

¥The treemap shows each module as a rectangle. Where the size of the module is represented by the area of the rectangle.

点击模块即可查看其大小、检查其完整的导入链以及在应用中的具体使用位置:

¥Click a module to see its size, inspect its full import chain and see exactly where it’s used in your application:

步骤 4:将输出写入磁盘以便共享或比较差异

¥Step 4: Write output to disk for sharing or diffing

如果你想与团队成员共享分析结果或比较优化前后的包大小,你可以跳过交互式视图,并将分析结果保存为带有 --output 标志的静态文件:

¥If you want to share the analysis with teammates or compare bundle sizes before/after optimizations, you can skip the interactive view and save the analysis as a static file with the --output flag:

bash
npx next experimental-analyze --output

此命令会将输出写入 .next/diagnostics/analyze。你可以将此目录复制到其他位置以比较结果:

¥This command writes the output to .next/diagnostics/analyze. You can copy this directory elsewhere to compare results:

bash
cp -r .next/diagnostics/analyze ./analyze-before-refactor

Bundle Analyzer 还有更多选项,请参阅 Next.js CLI 参考文档以获取完整列表。

¥More options are available for the Bundle Analyzer, see Next.js CLI reference docs for the full list.

Webpack 的 @next/bundle-analyzer

¥@next/bundle-analyzer for Webpack

@next/bundle-analyzer 是一个插件,可帮助你管理应用包的大小。它会生成每个包及其依赖的大小的可视化报告。你可以使用该信息来删除大型依赖、拆分或 lazy-load 你的代码。

¥The @next/bundle-analyzer is a plugin that helps you manage the size of your application bundles. It generates a visual report of the size of each package and their dependencies. You can use the information to remove large dependencies, split, or lazy-load your code.

步骤 1:安装

¥Step 1: Installation

通过运行以下命令安装插件:

¥Install the plugin by running the following command:

bash
npm i @next/bundle-analyzer
# or
yarn add @next/bundle-analyzer
# or
pnpm add @next/bundle-analyzer

然后,将打包分析器的设置添加到你的 next.config.js

¥Then, add the bundle analyzer's settings to your next.config.js.

步骤 2:生成报告

¥Step 2: Generating a report

运行以下命令来分析你的包:

¥Run the following command to analyze your bundles:

bash
ANALYZE=true npm run build
# or
ANALYZE=true yarn build
# or
ANALYZE=true pnpm build

该报告将在你的浏览器中打开三个新选项卡,你可以检查这些选项卡。

¥The report will open three new tabs in your browser, which you can inspect.

优化大型包

¥Optimizing large bundles

一旦你确定了一个大型模块,解决方案将取决于你的具体用例。以下是常见原因及解决方法:

¥Once you've identified a large module, the solution will depend on your use case. Below are common causes and how to fix them:

包含大量导出的包

¥Packages with many exports

如果你使用的包导出数百个模块(例如图标库和实用程序库),你可以使用 next.config.js 文件中的 optimizePackageImports 选项来优化这些导入的解析方式。此选项将仅加载你实际使用的模块,同时仍为你提供编写具有许多命名导出的导入语句的便利。

¥If you're using a package that exports hundreds of modules (such as icon and utility libraries), you can optimize how those imports are resolved using the optimizePackageImports option in your next.config.js file. This option will only load the modules you actually use, while still giving you the convenience of writing import statements with many named exports.

需要了解:Next.js 还会自动优化一些库,因此无需将它们添加到 optimizePackageImports 列表中。请参阅 完整列表 支持的软件包列表。

¥Good to know: Next.js also optimizes some libraries automatically, thus they do not need to be included in the optimizePackageImports list. See the full list of supported packages.

高负载客户端

¥Heavy client workloads

客户端包过大的常见原因是客户端组件中执行了耗时的渲染工作。这种情况通常发生在仅用于将数据转换为 UI 的库中,例如语法高亮、图表渲染或 Markdown 解析。

¥A common cause of large client bundles is doing expensive rendering work in Client Components. This often happens with libraries that exist only to transform data into UI, such as syntax highlighting, chart rendering, or markdown parsing.

如果该工作不需要浏览器 API 或用户交互,则可以在服务器组件中运行。

¥If that work does not require browser APIs or user interaction, it can be run in a Server Component.

在这个例子中,一个基于 Prism 的高亮器在客户端组件中运行。即使最终输出只是一个 <code> 代码块,整个高亮库也会被打包到客户端 JavaScript 包中:

¥In this example, a prism based highlighter runs in a Client Component. Even though the final output is just a <code> block, the entire highlighting library is bundled into the client JavaScript bundle:

tsx
'use client'

import Highlight from 'prism-react-renderer'
import theme from 'prism-react-renderer/themes/github'

export default function Page() {
  const code = `export function hello() {
    console.log("hi")
  }`

  return (
    <article>
      <h1>Blog Post Title</h1>

      {/* The prism package and its tokenization logic are shipped to the client */}
      <Highlight code={code} language="tsx" theme={theme}>
        {({ className, style, tokens, getLineProps, getTokenProps }) => (
          <pre className={className} style={style}>
            <code>
              {tokens.map((line, i) => (
                <div key={i} {...getLineProps({ line })}>
                  {line.map((token, key) => (
                    <span key={key} {...getTokenProps({ token })} />
                  ))}
                </div>
              ))}
            </code>
          </pre>
        )}
      </Highlight>
    </article>
  )
}

此功能会增加包的大小,因为客户端必须下载并执行高亮库,即使结果是静态 HTML。

¥This increases bundle size because the client must download and execute the highlighting library, even though the result is static HTML.

而是将高亮逻辑移至服务器组件,并在服务器端渲染最终 HTML。客户端将仅接收渲染后的标记。

¥Instead, move the highlighting logic to a Server Component and render the final HTML on the server. The client will only receive the rendered markup.

tsx
import { codeToHtml } from 'shiki'

export default async function Page() {
  const code = `export function hello() {
    console.log("hi")
  }`

  // The Shiki package runs on the server and is never bundled for the client.
  const highlightedHtml = await codeToHtml(code, {
    lang: 'tsx',
    theme: 'github-dark',
  })

  return (
    <article>
      <h1>Blog Post Title</h1>

      {/* Client receives plain markup */}
      <pre>
        <code dangerouslySetInnerHTML={{ __html: highlightedHtml }} />
      </pre>
    </article>
  )
}

选择特定包不进行打包

¥Opting specific packages out of bundling

在服务器组件和路由处理程序中导入的包由 Next.js 自动打包。

¥Packages imported inside Server Components and Route Handlers are automatically bundled by Next.js.

你可以使用 next.config.js 中的 serverExternalPackages 选项选择不打包特定软件包。

¥You can opt specific packages out of bundling using the serverExternalPackages option in your next.config.js.