首页
/ Next.js Bundle Analyzer:基于交互式 Treemap 的 Bundle 体积与依赖分析工具深度解析

Next.js Bundle Analyzer:基于交互式 Treemap 的 Bundle 体积与依赖分析工具深度解析

2026-09-04 23:12:54作者:农烁颖Land

本篇技术文章聚焦 Next.js 仓库中的 Bundle Analyzer 应用(apps/bundle-analyzer/README.md):它是一个专门用于可视化 bundle 体积、分析依赖关系的 Next.js 应用。读完后你将掌握它的五大核心功能(交互式 Treemap、路由级分析、依赖链追踪、环境/文件类型过滤、文件搜索)、它与主 next 包"构建期内嵌(vendor)"的分发机制,以及底层二进制分析数据格式(analyze.data / modules.data)的解析原理,并知道如何通过 next experimental-analyze 在实际项目中使用它。

它是什么:一个内嵌于 next 包的独立 Next.js 应用

根据 README 的定义,Bundle Analyzer 是:

A Next.js application for visualizing bundle sizes and analyzing dependencies using interactive treemaps.

package.json 可以确认其工程属性:

  • 包名为 @next/bundle-analyzer-ui,且声明了 "private": true —— 它不会发布到 npm
  • README 明确指出:"This package is not published to npm. Instead it's built and vendored into the main next package during its build process."(它不发布到 npm,而是在主 next 包的构建过程中被构建并内嵌进去);
  • 它本身就是一个用 React 19 + Tailwind CSS 4 + Radix UI 组件构建的完整 Next.js App Router 应用,入口为 app/page.tsxapp/layout.tsx(页面元数据标题即为 "Next.js Bundle Analyzer")。

从源码结构看"vendor 内嵌"是如何落地的

这一分发机制在仓库中有两处源码证据:

  1. 构建依赖声明packages/next/turbo.json 中,主 next 包的 build 任务声明了 "dependsOn": ["@next/bundle-analyzer-ui#build", "^build"],即每次构建 next 包时会先触发 Bundle Analyzer UI 的构建(其构建脚本为 next build --no-mangling,见 package.json 的 scripts 字段)。
  2. 运行时复制:在 packages/next/src/build/analyze/index.ts 中,analyze 函数完成 Turbopack 分析后执行:
await cp(path.join(__dirname, '../../bundle-analyzer'), analyzeDir, {
  recursive: true,
})

也就是说,next 包内 dist/bundle-analyzer 目录(即构建期内嵌的静态站点)会被整体复制到目标项目的 .next/diagnostics/analyze 目录下,与 data/ 数据目录放在一起供本地服务器托管。

核心功能:README 五大特性的源码印证

README 的 Features 一节列出了五项核心能力,下文逐项结合源码展开。

1. 交互式 Treemap(Interactive Treemap)

页面主体由 TreemapVisualizer 组件渲染,布局算法实现在 lib/treemap-layout.tslib/layout-treemap.ts 中。从 page.tsx 的渲染代码可以看到,Treemap 以 SizeMode.Compressed(压缩后体积)作为默认尺寸模式:

<TreemapVisualizer
  analyzeData={analyzeData}
  sourceIndex={rootSourceIndex}
  selectedSourceIndex={selectedSourceIndex ?? rootSourceIndex}
  ...
  sizeMode={SizeMode.Compressed}
/>

交互细节还包括:

  • 底部状态栏实时显示悬停文件的名称、压缩体积以及 client / server / traced 徽标(见 page.tsx 底部 hoveredNodeInfo 渲染逻辑);
  • Escape 键可将 Treemap 选区重置回当前路由的根 source(handleKeyDown 逻辑);
  • 右侧详情面板(Sidebar)宽度可在 10%–50% 之间拖拽调整。

2. 路由级分析(Route-based Analysis)

分析数据按路由组织存储:根路由读取 data/analyze.data,其余路由读取 data/<route>/analyze.datapage.tsx 中的数据路径推导逻辑:

let analyzeDataPath
if (selectedRoute && selectedRoute === '/') {
  analyzeDataPath = 'data/analyze.data'
} else if (selectedRoute) {
  analyzeDataPath = `data/${selectedRoute.replace(/^\//, '')}/analyze.data`
}

路由列表来源是 packages/next/src/build/analyze/index.tscollectRoutesForAnalyze 函数生成的 routes.json:它同时扫描 apppages 目录(支持 hybrid 项目),经 discoverRoutes + generateRoutesManifest 得到全部静态/动态路由后序列化写入 analyzeDir/data/routes.json。UI 侧由 RouteTypeahead 组件提供路由输入联想。数据文件类型定义(RouteManifest)见 lib/types.ts

3. 依赖链追踪(Dependency Tracking)

点击某个文件后,import-chain 组件基于模块图展示导入链路与依赖关系。底层计算依赖 lib/module-graph.ts 中的 computeActiveEntriescomputeModuleDepthMap,数据则来自全局共享的 modules.data(所有路由共用一份模块表,每路由只持有自己的 analyze.data):

const { data: modulesData } = useSWR<ModulesData>('data/modules.data', fetchModulesData)

4. 过滤控制(Filter Controls)

README 说明可以按环境(client/server)和文件类型(JS/CSS/JSON/Assets)过滤。page.tsx 顶栏提供环境单选(Monitor/Server 图标对应的 Client/Server)与文件类型多选(JavaScript/CSS/JSON/Asset),过滤谓词 filterSource 通过 analyzeData.getSourceFlags(sourceIndex) 判定每个 source 的归属:

const hasEnvironment =
  (environmentFilter === Environment.Client && flags.client) ||
  (environmentFilter === Environment.Server && flags.server)

getSourceFlags 的判定规则(见 lib/analyze-data.ts)颇具信息量:

  • 输出文件以 [client-fs]/ 开头 → 标记为 client
  • [project]/ 开头 → 标记为 server + traced(即被追踪保留到运行时的服务端文件);
  • 其余 → server
  • 按扩展名区分类型:.js/.mjs/.cjs → js,.css → css,.json → json,其他 → asset。

5. 文件搜索(Search Functionality)

FileSearch 组件(基于 cmdk 实现)对 bundle 内文件进行搜索,搜索词 searchQuery 传入 Treemap 高亮匹配项。文件索引由 AnalyzeData 构造时建立的 pathToSourceIndex 路径映射与 ModulesDatapathToModuleIndex 映射提供支撑。

数据协议:JSON 头 + 二进制边的 .data 文件格式

这是本应用最有技术含量的部分。lib/analyze-data.ts 注释说明这些类型"匹配 Rust 侧 analyze.rs 的结构"——即 Turbopack(Rust)产出分析数据,TypeScript 侧负责解析与渲染。文件布局为:

[ u32: JSON 头长度 ][ JSON 头 ][ 二进制边数据(u32 数组) ]

其中:

  • AnalyzeDataHeader(analyze.data)sources(source 树,含 parent_source_index 组成目录层级)、chunk_parts(每个 chunk 分片记录 source_index/output_file_index/size/compressed_size)、output_files(输出文件名)、三组 EdgesDataReferenceoutput_file_chunk_partssource_chunk_partssource_children)以及 source_roots
  • ModulesDataHeader(modules.data)modules 表加上 module_dependentsmodule_dependencies 及其 async/traced 变体共 6 组边引用。

readEdgesDataAtIndex 用 CSR(压缩稀疏行)风格读取边表:只读取目标索引所需的前后两个 offset,避免整表反序列化,使单文件依赖查询接近 O(出度) 而非 O(总边数)。在此基础上封装了 getOwnSizes / getRecursiveSizes(递归体积,支持过滤谓词)、sourceChunks(所在 chunk 列表)、getRecursiveModuleCount 等 API,供 Treemap 布局与侧边栏统计调用。

如何使用:next experimental-analyze 的完整链路

虽然本应用"不发布 npm",但它是 next experimental-analyze 命令的可视端。从 packages/next/src/build/analyze/index.ts 可以还原完整调用链与参数:

export type AnalyzeOptions = {
  dir: string
  reactProductionProfiling?: boolean
  noMangling?: boolean
  appDirOnly?: boolean
  output?: boolean
  port?: number
}
参数 默认值 作用
dir - 项目目录,.next 作为 distDir
noMangling false 不做符号混淆,便于阅读模块名
appDirOnly false 仅分析 app 目录路由(路由收集时传入 discoverRoutes
output false true 时仅产出结果文件到 .next/diagnostics/analyze,不启动服务器
port 4000 交互式结果服务器的监听端口(仅监听 localhost

执行流程:

  1. 强制 process.env.TURBOPACK ??= '1' —— analyze 是 Turbopack 专属能力(源码注释:"analyze is Turbopack-only");
  2. PHASE_ANALYZE 阶段加载项目配置,调用 turbopackAnalyze 执行生产构建级分析并返回耗时;
  3. next 包内嵌的 bundle-analyzer 静态站点复制到 .next/diagnostics/analyze,写入 data/routes.json
  4. 若未指定 output,调用 startServer:基于 serve-handlerlocalhost:<port>(默认 4000)启动静态服务器,打印 Bundle analyzer available at http://<address> 后阻塞等待。

开发该应用本身时,next.config.mjs 有一个值得注意的配置:开发模式下将 /data/:path* 重写代理到 http://localhost:4000/data/:path*,即从正在运行的 next experimental-analyze 服务器实时拉取分析数据调试 UI;而构建发布时使用 output: 'export' 导出纯静态站点到 dist。此外构建脚本携带 NEXT_TEST_NATIVE_IGNORE_LOCAL_INSTALL=true 环境变量,表明它使用 next 包内置的原生二进制而非本地工作区安装。

维护约定:重要变更需同步更新 Demo 站点

README 的 Updating 一节给出了一条明确的维护约定:当落地非平凡(non-trivial)的改动时,应考虑同步更新官方 demo 站点(README 中附带了 demo 站点的 URL 与独立仓库地址,此处不再重复外链)。这说明 Bundle Analyzer 的 UI 迭代是以一个独立演示仓库为回归/展示基线的。

小结

  • Bundle Analyzer 是 Next.js 仓库中一个私有、构建期内嵌next 包的工具型 Next.js 应用,服务于 next experimental-analyze 命令的交互式结果展示;
  • 其数据协议由 Turbopack(Rust)以"JSON 头 + CSR 边表"的二进制格式产出,TypeScript 端按需解析,兼顾体积与查询效率;
  • 功能面覆盖 Treemap 可视化、按路由下钻、import 依赖链、client/server 环境与 JS/CSS/JSON/Asset 类型过滤、文件搜索,并支持 --output 离线产出与 --port 自定义端口;
  • 相关关键文件:README数据解析类型定义路由与模块图分析入口

适用前提说明:analyze 功能基于 Turbopack,且分析的是生产构建(源码日志提示 "Analyzing a production build..."),在开发本仓库内的该应用需按 package.json 中的 scripts 执行并配合 localhost:4000 的数据服务。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384