source-map-explorer logo

source-map-explorer

通过 Source Map 分析 JavaScript 打包产物体积构成的可视化调试工具

Bundle分析性能优化Source Map可视化

通过解析打包产物的 Source Map 文件,将压缩混淆后的代码反向映射回原始源文件,精确计算每个源文件在最终 bundle 中所占的字节数,以交互式 Treemap 形式呈现。

source-map-explorer

通过 Source Map 分析 JavaScript 打包产物体积构成的可视化调试工具——定位你的 bundle 里每一字节到底来自哪个源文件。

技术简介说明

source-map-explorer 是一个用于分析和调试 JavaScript(或 Sass/LESS)代码体积膨胀问题的命令行工具。它的核心工作原理是解析打包产物的 Source Map 文件,将压缩混淆后的代码反向映射回原始源文件,精确计算出每个源文件在最终 bundle 中所占的字节数,并以交互式 Treemap 的形式呈现出来。

与其他依赖打包工具内部统计信息的分析工具不同,source-map-explorer 的独特价值在于它工作在"最终产物"层面——它看到的是经过压缩、Tree-shaking、死代码消除等所有优化步骤之后的真实结果。这意味着它给出的分析结果是最接近用户在浏览器中实际下载内容的"ground truth"。这种打包工具无关(bundler-agnostic)的特性,使得它可以在 Webpack、Rollup、Vite、esbuild、Parcel 等任意打包器的产出的基础上进行分析。

该工具由 Dan Vanderkam(GitHub: danvk)创建于 2014 年,最初作为前端性能优化的个人项目,后来成为 Create React App 官方推荐的 bundle 分析工具之一,在 Angular 社区也被官方建议使用。截至目前,npm 周下载量约 150 万次,GitHub 获得约 3,900+ Stars。但自 2022 年 9 月发布 v2.5.3 后,项目已近 4 年未发布新版本,处于事实上的维护停滞状态。

基本信息

快速上手

安装

全局安装(推荐,CLI 使用):

npm install -g source-map-explorer
 
# 或使用 yarn
yarn global add source-map-explorer

项目本地安装(作为开发依赖):

npm install --save-dev source-map-explorer
 
# 或使用 yarn
yarn add --dev source-map-explorer

无需安装,直接使用 npx:

npx source-map-explorer bundle.min.js

基础配置

确保打包产物包含 Source Map

source-map-explorer 依赖 Source Map 文件工作,需要在打包时启用 Source Map 生成:

Webpack 配置:

// webpack.config.js
module.exports = {
  // ...
  devtool: 'source-map', // 生成完整的 source map
  // 其他选项如 'hidden-source-map' 也可,但不能为 false
}

Vite 配置:

// vite.config.js
export default defineConfig({
  build: {
    sourcemap: true, // 生成 source map
  },
})

Rollup 配置:

// rollup.config.js
export default {
  // ...
  output: {
    sourcemap: true,
  },
}

Create React App:

# 默认开发构建会生成 source map
# 生产构建需设置环境变量
GENERATE_SOURCEMAP=true npm run build

Angular CLI:

# 默认生产构建会生成 source map
ng build --configuration production
# 或显式开启
ng build --source-map=true

最小示例

分析单个 bundle 文件(自动查找关联的 .map 文件):

source-map-explorer dist/bundle.min.js

命令会自动在浏览器中打开一个交互式的 Treemap 可视化界面,展示每个源文件在 bundle 中的体积占比。

显式指定 source map 文件:

source-map-explorer dist/bundle.min.js dist/bundle.min.js.map

使用通配符分析多个文件:

# 分析 dist 目录下所有 JS 文件
source-map-explorer dist/*.js
 
# 使用 bundle 通配符(自动匹配 .js 和 .map)
source-map-explorer dist/bundle.min.js*

核心概念与架构

Source Map 解析原理

Source Map 是一种存储源代码与压缩/编译代码之间位置映射关系的文件(通常为 .map 文件)。source-map-explorer 的工作流程如下:

  1. 读取打包产物:读取压缩后的 JS 文件(如 bundle.min.js
  2. 定位 Source Map
    • 内联 Source Map:解析文件末尾的 //# sourceMappingURL=data:application/json;base64,... 注释
    • 外部 Source Map:解析 //# sourceMappingURL=bundle.min.js.map 引用并读取对应文件
    • 手动指定:通过命令行参数直接传入 .map 文件路径
  3. 解析 Source Map:使用 mozilla/source-map 库解析 VLQ 编码的映射数据,建立压缩后位置到原始源文件的对应关系
  4. 字节统计:逐字节(或逐 token)统计每个源文件在压缩产物中贡献的字节数
  5. 分类标记:将无法映射的字节归类为:
    • [unmapped]:未在 Source Map 中找到映射的字节(如 Webpack 按需加载 chunk 的逻辑代码)
    • [no source]:有映射但无原始源文件的代码(如打包器自身生成的 runtime 代码)
    • [sourceMappingURL]:Source Map 引用注释本身的字节
    • [EOLs]:换行符占用的字节
  6. 可视化渲染:将统计结果渲染为交互式 Treemap,使用 d3.js 实现

核心数据结构

工具内部通过 explore() 函数返回一个 Promise,解析后包含以下信息:

interface ExploreResult {
  bundles: Array<{
    bundleName: string        // bundle 文件名
    totalBytes: number        // 总字节数
    mappedBytes: number       // 已映射字节数
    unmappedBytes: number     // 未映射字节数
    eolBytes: number          // 换行符字节数
    sourceMapCommentBytes: number  // source map 注释字节数
    files: Map<string, number>     // 每个源文件 → 字节数映射
  }>
  errors: Array<{
    bundleName: string
    code: string              // 错误代码
    message: string
    isWarning: boolean        // 是否为警告(如 UnmappedBytes)
  }>
}

核心特性

  • 打包工具无关(Bundler-Agnostic):不依赖特定打包器的内部统计,只要有 Source Map 就能分析。支持 Webpack、Rollup、Vite、esbuild、Parcel、Browserify、Create React App、Angular CLI、Metro(React Native)等任意打包器的产物
  • 交互式 Treemap 可视化:基于 d3.js 的浏览器端 Treemap,支持缩放、悬停查看详情,直观展示每个源文件的体积占比
  • 多种输出格式:支持 HTML(可视化)、JSON(程序化分析)、TSV(表格工具导入)三种输出格式,可输出到 stdout 或直接保存为文件
  • Gzip 体积计算:通过 --gzip 标志计算各文件的 gzip 压缩后体积,更贴近实际传输大小
  • 代码覆盖率热力图:通过 --coverage 参数导入 Chrome DevTools 导出的覆盖率 JSON,在 Treemap 上以颜色编码展示代码使用情况(绿色 = 页面加载时使用,红色 = 用户交互时才使用),辅助识别可 Code Split 的代码
  • Source Map 路径替换:通过 --replace--with 参数进行路径查找替换(支持正则),清理可视化中的路径冗余
  • Node.js 编程式 API:提供 explore() 函数返回 Promise,可以在 Node.js 脚本、构建流程或 CI 中程序化调用
  • Glob 模式支持:支持一次分析多个 bundle 文件,使用通配符匹配
  • 内联/外部 Source Map 支持:自动识别并解析内联(base64 data URL)和外部(.map 文件)两种 Source Map 形式
  • 公共路径前缀自动去除:默认去除所有文件路径的公共前缀,使 Treemap 更简洁(可通过 --no-root 禁用)

生态图

关键依赖

依赖用途
glob文件通配符匹配
source-mapMozilla 出品的 Source Map 解析库,核心依赖
d3用于 Treemap 可视化的数据驱动文档库
lodash工具函数库
bzip2-minimalgzip 体积计算
ejsHTML 报告模板渲染
tempy临时文件创建(用于打开浏览器可视化)
yargsCLI 参数解析

相关工具

工具说明
source-map-visualization基于 Web 的 Source Map 调试可视化工具
bundle-wizardWeb 应用入口点分析工具,内部使用 source-map-explorer
Sourcemap Explorer by MapRee基于 Web 的在线 Source Map 分析工具,可检测任意站点的 sourcemap
exorcist将内联 Source Map 抽取为外部 .map 文件的工具

社区生态

  • Create React App 官方文档推荐为 bundle 分析工具
  • Angular CLI 社区建议使用(比 webpack-bundle-analyzer 更准确)
  • 在 React、Angular、Vue 等主流框架社区均有广泛使用教程
  • 常作为前端性能优化文章和课程中的标准工具被引用

适用场景

  • 生产 Bundle 体积审计:在发布前精确分析生产构建中每个模块的实际体积贡献,找出体积膨胀的"元凶"。由于基于 Source Map 分析,它反映的是经过所有优化后的真实产物,比打包器内部统计更准确
  • 跨打包工具对比分析:当项目从 Webpack 迁移到 Vite/Rollup,或使用多种打包器时,可以用统一的方式对比不同打包器产出的体积构成
  • 代码覆盖率辅助 Code Splitting:结合 --coverage 参数导入 Chrome 覆盖率数据,通过热力图识别"加载时用不到"的代码,指导路由级或组件级的代码分割策略
  • Tree-shaking 效果验证:分析 Tree-shaking 后仍被打包进来的代码,检查是否有模块因副作用(side effects)或错误引用而无法被正确 shake 掉
  • 依赖体积贡献分析:快速定位哪些 npm 包占用了过多空间,为依赖替换(如 moment.js → dayjs)或按需引入(如 lodash-es 替代 lodash)提供数据支撑
  • CI/CD 体积预算检查:利用 JSON 输出格式在 CI 流程中自动化体积检查,设定体积阈值并在超出时构建失败
  • Source Map 质量验证:分析 unmapped bytes 的比例,评估打包器生成的 Source Map 的完整性和准确性

开发与工程化

构建流程

source-map-explorer 本身使用 TypeScript 编写,构建流程:

# 克隆仓库
git clone https://github.com/danvk/source-map-explorer.git
cd source-map-explorer
 
# 安装依赖
npm install
 
# 构建 TypeScript
npm run build
 
# 运行测试
npm test
 
# 本地运行 CLI
node dist/cli.js <bundle-file>

工程化最佳实践

在 package.json 中添加分析脚本

{
  "scripts": {
    "build": "webpack --mode production",
    "analyze": "source-map-explorer 'dist/*.js'",
    "analyze:json": "source-map-explorer 'dist/*.js' --json > bundle-analysis.json",
    "analyze:gzip": "source-map-explorer 'dist/*.js' --gzip"
  }
}

CI/CD 集成示例(GitHub Actions)

name: Bundle Analysis
on: [pull_request]
 
jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
 
      - run: npm ci
      - run: npm run build
 
      - name: Analyze bundle
        run: |
          npx source-map-explorer 'dist/*.js' --json > bundle-analysis.json
 
      - name: Check bundle size
        run: |
          # 从 JSON 中提取总大小并检查阈值
          node -e "
            const data = require('./bundle-analysis.json');
            const totalSize = Object.values(data).reduce((sum, b) => sum + b.totalBytes, 0);
            const maxSize = 500 * 1024; // 500KB
            if (totalSize > maxSize) {
              console.error('Bundle size exceeds budget:', totalSize, 'bytes');
              process.exit(1);
            }
            console.log('Bundle size OK:', totalSize, 'bytes');
          "

编程式 API 使用

import { explore } from 'source-map-explorer'
 
// 基础用法
const result = await explore('dist/bundle.js')
console.log(result.bundles[0].totalBytes)
console.log(result.bundles[0].files)
 
// 输出为 JSON 文件
await explore('dist/bundle.js', {
  output: { format: 'json', filename: 'analysis.json' }
})
 
// 排除未映射字节,仅关注有 Source Map 映射的部分
await explore('dist/bundle.js', { onlyMapped: true })
 
// 路径替换,清理 webpack 前缀
await explore('dist/bundle.js', {
  replaceMap: { 'webpack:///': '' }
})
 
// 使用 bundle 对象作为输入
import fs from 'fs'
await explore({
  code: fs.readFileSync('dist/bundle.js'),
  map: fs.readFileSync('dist/bundle.js.map')
})

性能与安全

性能特点

  • 大型 bundle 处理:对于几 MB 大小的 bundle,分析通常在数秒内完成。Source Map 解析是计算密集型操作,时间复杂度与 bundle 大小和 Source Map 中的映射数量成正比
  • 内存占用:Source Map 解析过程需要将整个 map 文件加载到内存中。对于大型项目(Source Map 文件超过 50MB),可能占用较多内存
  • Gzip 计算近似性:使用 --gzip 时,各文件 gzip 体积之和可能略小于整体 gzip 体积,因为 gzip 对整体压缩效率更高。移除一个 1KB 的 gzip 文件可能只减少不到 1KB 的总 gzip 体积

Source Map 安全性

⚠️ 重要安全警告:Source Map 文件包含完整的原始源代码。在生产环境中暴露 .map 文件会导致未压缩的源代码、业务逻辑、硬编码密钥、API 端点等敏感信息泄露。

安全风险:

  • 攻击者可通过访问 bundle.js.map 完整还原原始源代码,大幅降低攻击门槛
  • 内部 API 端点、密钥、业务逻辑、管理后台路径等敏感信息将暴露
  • 2025-2026 年有多起知名企业(包括 GitHub、Anthropic 等)因意外暴露 Source Map 而被安全社区关注的事件

最佳安全实践:

  • 生产环境禁止公开 .map 文件:在 Web 服务器配置中禁止 .map 文件的公开访问
    # Nginx 配置
    location ~* \.map$ {
      deny all;
      return 404;
    }
  • 使用 hidden-source-map:生成 Source Map 但不添加 sourceMappingURL 注释
    // webpack.config.js
    devtool: 'hidden-source-map'
  • 上传到错误监控服务:将 Source Map 上传到 Sentry、Datadog 等错误追踪平台,而非部署到公开服务器
  • 构建后删除 .map 文件:在使用 source-map-explorer 分析完毕后,从部署产物中删除 .map 文件
  • 仅本地分析:source-map-explorer 的分析过程完全在本地运行,不需要将 Source Map 上传到任何第三方服务

使用 source-map-explorer 时的安全建议

  • 在本地或 CI 环境中运行分析,不要在生产服务器上执行
  • 分析完成后,确保 .map 文件不包含在部署产物中
  • 对于开源项目,暴露 Source Map 的风险较低,但仍需评估

技术对比

与同类工具对比

特性source-map-explorerwebpack-bundle-analyzerrollup-plugin-visualizersonda
最新版本v2.5.3(2022-09)v5.3.0(活跃维护)v7.0.1(活跃维护)较新
GitHub Stars~3,900~12,700~2,400
工作原理解析 Source MapWebpack stats.jsonRollup 构建统计多打包器支持
打包器兼容性✅ 任意(Webpack/Rollup/Vite/esbuild/Parcel 等)❌ 仅 Webpack⚠️ 主要 Rollup/Vite/Rolldown,新支持 esbuild/Webpack✅ esbuild/Webpack/Rspack/Rollup/Vite
分析精度⭐⭐⭐⭐⭐ 基于最终产物⭐⭐⭐ 基于打包器内部统计(可能不准确)⭐⭐⭐⭐ 基于 Rollup 统计⭐⭐⭐⭐
可视化类型TreemapTreemap(可交互缩放)Treemap/Sunburst/Network/3DTreemap
输出格式HTML/JSON/TSVHTML/JSON/静态报告HTML/SVG/JSON/TextHTML
CLI 独立运行✅ 支持❌ 需作为 Webpack 插件❌ 需作为 Rollup 插件✅ 支持
代码覆盖率热力图✅ 支持
Gzip 体积✅ 支持✅ 支持(显示 gzip/parsed 体积)✅ 支持
编程式 API✅ explore() 返回 Promise✅ 可程序化调用✅ 插件 API
维护状态⚠️ 停滞(~4年未更新)✅ 活跃✅ 活跃✅ 活跃
npm 周下载量~150 万~975 万~414 万较少

何时选择 source-map-explorer

  • ✅ 需要最精确的 bundle 分析结果(基于最终产物而非打包器统计)
  • ✅ 使用多种打包器或正在从 Webpack 迁移到其他工具
  • ✅ 需要分析已有的 bundle 文件(事后分析,无需重新构建)
  • ✅ 使用 Angular 等官方推荐使用 source-map-explorer 的框架
  • ✅ 需要代码覆盖率热力图功能辅助 Code Splitting

何时不选择 source-map-explorer

  • ❌ 需要开发时的实时交互式分析(选 webpack-bundle-analyzer 或 rollup-plugin-visualizer)
  • ❌ 项目使用 Vite/Rollup 且需要丰富的可视化类型(选 rollup-plugin-visualizer)
  • ❌ 需要活跃维护和新特性(source-map-explorer 已停滞 ~4 年)
  • ❌ 需要与打包器深度集成的分析(如 chunk splitting 决策分析)

最佳实践

生产环境使用建议

1. 将分析作为构建流程的一部分

{
  "scripts": {
    "build": "webpack --mode production",
    "postbuild": "source-map-explorer 'dist/*.js' --html bundle-report.html"
  }
}

2. 建立体积预算(Size Budget)

// scripts/bundle-budget.js
import { explore } from 'source-map-explorer'
 
const BUDGET = {
  maxTotalSize: 500 * 1024,      // 总大小不超过 500KB
  maxSingleFile: 200 * 1024,     // 单文件不超过 200KB
  maxUnmappedRatio: 0.05,        // 未映射字节不超过 5%
}
 
const result = await explore('dist/*.js', { onlyMapped: false })
 
for (const bundle of result.bundles) {
  if (bundle.totalBytes > BUDGET.maxTotalSize) {
    console.error(`❌ ${bundle.bundleName} 超出体积预算: ${bundle.totalBytes} bytes`)
    process.exit(1)
  }
  const unmappedRatio = bundle.unmappedBytes / bundle.totalBytes
  if (unmappedRatio > BUDGET.maxUnmappedRatio) {
    console.warn(`⚠️ ${bundle.bundleName} 未映射比例过高: ${(unmappedRatio * 100).toFixed(1)}%`)
  }
}

3. 对比前后版本差异

# 分析 main 分支的产物
git checkout main
npm run build
source-map-explorer 'dist/*.js' --json > main-analysis.json
 
# 分析当前分支的产物
git checkout feature-branch
npm run build
source-map-explorer 'dist/*.js' --json > current-analysis.json
 
# 使用 diff 工具或脚本对比两个 JSON 的差异

4. 使用路径替换清理可视化

# 清理 webpack 路径前缀,让 Treemap 更可读
source-map-explorer 'dist/*.js' --replace 'webpack:///' --with ''
 
# 清理 node_modules 路径
source-map-explorer 'dist/*.js' --replace '.*node_modules/' --with ''

常见陷阱

  • 忘记开启 Source Map:如果打包时 devtool: false,source-map-explorer 将无法工作。确保构建时生成 Source Map
  • 忽视 unmapped bytes:大量未映射字节通常意味着 Source Map 不完整或打包器的某些配置有问题,应调查原因而非简单忽略
  • Gzip 体积的误解:各文件 gzip 体积之和 ≠ 整体 gzip 体积,因为 gzip 整体压缩有字典共享优势
  • 生产环境遗留 .map 文件:使用 source-map-explorer 分析后,务必从部署产物中删除 .map 文件,避免安全风险
  • 过大的 Source Map 文件:某些打包器配置可能生成非常大的 Source Map,导致分析时内存占用过高

推荐模式

  • 在 CI 中使用 --json 输出进行自动化体积检查,而不是依赖人工查看 Treemap
  • 结合 --gzip 分析实际传输体积,因为用户下载的是压缩后的内容
  • 使用 --coverage 功能配合 Code Splitting 决策,直观看到哪些代码是"冷启动"用不到的
  • 将 source-map-explorer 作为"最终审计工具",配合开发时的 webpack-bundle-analyzer 或 rollup-plugin-visualizer 使用

技术局限与边界

已知限制

  • ** unmapped bytes 问题**:某些打包器(特别是 Webpack)的动态 import(import())产生的 chunk 加载代码可能无法映射,导致较大比例的 [unmapped] 区域
  • Source Map v3 限制:仅支持 Source Map v3 格式,不支持实验性的 v4 提案中的新特性
  • 无实时监控能力:仅支持事后分析(post-build),无法在开发过程中实时监控 bundle 变化
  • Treemap 可视化单一:仅支持 Treemap 一种可视化类型,不提供 Sunburst、Network 图等替代视图
  • 大文件性能瓶颈:Source Map 文件超过数十 MB 时,解析内存占用较高,可能出现性能问题
  • 不支持 CSS Source Map 分析:虽然文档提到支持 Sass/LESS,但对 CSS/SCSS Source Map 的支持有限,主要用于 JS 分析

不适用场景

  • 开发时实时分析:需要实时反馈的场景请使用 webpack-bundle-analyzer 的开发服务器模式或 rollup-plugin-visualizer 的 watch 模式
  • Library 体积分析:分析 npm 包的体积时,bundlejs.comsize-limit 更合适
  • 运行时性能分析:source-map-explorer 只分析静态体积,不涉及运行时性能、渲染性能或内存占用
  • 网络传输分析:不分析实际的网络加载性能(如 TTFB、带宽限制等),需要 Lighthouse 或 WebPageTest 等工具

维护风险

截至 2026 年 7 月,source-map-explorer 已近 4 年未发布新版本。GitHub 上有 32 个 open issues25 个 open PRs 未处理。e18e.dev 项目已将其列为"需要替代品"的工具之一。虽然其核心功能仍然有效,但未来可能无法支持新版本的打包器或 Source Map 规范的更新。

权衡取舍

选择 source-map-explorer牺牲
分析精度高(基于最终产物)开发时交互体验差(无实时模式)
打包器无关需要额外构建步骤生成 Source Map
支持事后分析无法深入打包器内部决策过程
简单轻量可视化类型单一

学习资源

官方资源

官方推荐与集成

教程与文章

社区资源

Source Map 规范

2026 年现状

版本与维护状态

  • 最新版本:v2.5.3(2022-09-26 发布),已近 4 年未更新
  • 维护状态:事实停滞。32 个 open issues、25 个未合并的 PRs,无活跃的 maintainer 活动
  • npm 周下载量:约 150 万次(下载量稳定,说明存量用户众多,但新增长有限)
  • GitHub Stars:约 3,900+

发展趋势

  • 替代品涌现:e18e.dev 已推荐 rollup-plugin-visualizer、sonda、webpack-bundle-analyzer 作为替代方案
  • Vite/Rollup 生态崛起:随着 Vite 成为主流构建工具,rollup-plugin-visualizer 的使用量快速增长(周下载量 ~414 万),正在蚕食 source-map-explorer 的使用场景
  • 新兴工具竞争
  • Source Map 规范演进:TC39 正在推进 Source Map 规范化(v4 提案),source-map-explorer 作为停滞项目可能无法及时跟进新规范

社区活跃度

  • Issue 响应:Issues 基本无人回复,新提交的 issue 通常数周甚至数月无响应
  • PR 合并:无活跃的代码合并活动
  • Stack Overflow / 社区讨论:仍有开发者在提问和使用,但新增讨论量在减少
  • 作为存量工具:大量已有项目仍在使用,特别是在 Angular 和 Create React App 生态中

建议

如果你正在启动新项目或选择 bundle 分析工具:

  1. Vite/Rollup 项目:优先选择 rollup-plugin-visualizersonda
  2. Webpack 项目:继续使用 webpack-bundle-analyzer,辅以 source-map-explorer 做精确审计
  3. 需要精确事后分析:source-map-explorer 仍然是"精度最高"的选择,但需接受其维护风险
  4. 需要长期可维护的方案:关注 sonda 等新兴工具的演进

如果你已在项目中使用 source-map-explorer:

  • 短期内仍可继续使用,其核心功能稳定可靠
  • 考虑在 CI 中锁定版本([email protected])以避免未来潜在的依赖问题
  • 评估是否需要迁移到更活跃维护的替代方案