什么是源代码映射?

源代码映射是现代 Web 开发中的重要工具,可显著简化调试工作。本页将介绍源代码映射的基础知识、源代码映射的生成方式以及源代码映射如何改进调试体验。

需要源映射

早期的 Web 应用的复杂性较低。开发者直接将 HTML、CSS 和 JavaScript 文件部署到 Web 上。

更现代和更复杂的 Web 应用可能需要在其开发工作流中使用各种工具。例如:

各种工具的简要概览。
一些常见的 Web 应用开发工具。

这些工具需要构建流程来将您的代码转译为浏览器可以理解的标准 HTML、JavaScript 和 CSS。一种常见的做法是,使用 Terser 等工具缩小和合并这些文件以优化性能。

例如,使用构建工具,我们可以将以下 TypeScript 文件转译并压缩成一行 JavaScript。您可以自行试用 GitHub 上的此演示

/* A TypeScript demo: example.ts */

document.querySelector('button')?.addEventListener('click', () => {
  const num: number = Math.floor(Math.random() * 101);
  const greet: string = 'Hello';
  (document.querySelector('p') as HTMLParagraphElement).innerText = `${greet}, you are no. ${num}!`;
  console.log(num);
});

压缩后的版本将如下所示:

/* A compressed JavaScript version of the TypeScript demo: example.min.js  */

document.querySelector("button")?.addEventListener("click",(()=>{const e=Math.floor(101*Math.random());document.querySelector("p").innerText=`Hello, you are no. ${e}!`,console.log(e)}));

不过,压缩代码可能会使调试变得更加困难。源映射可以解决此问题:通过将编译后的代码映射回原始代码,源映射可以帮助您快速找到错误的来源。

生成源映射

源映射是名称以 .map 结尾的文件(例如 example.min.js.mapstyles.css.map)。大多数构建工具(包括 VitewebpackRollupParcelesbuild)都可以生成源映射。

某些工具默认包含源代码映射。其他报告可能需要进行额外的配置才能生成:

/* Example configuration: vite.config.js */
/* https://vitejs.dev/config/ */

export default defineConfig({
  build: {
    sourcemap: true, // enable production source maps
  },
  css: {
    devSourcemap: true // enable CSS source maps during development
  }
})

了解源映射

为了帮助调试,这些源映射文件包含有关编译代码如何映射到原始代码的重要信息。以下是源映射示例:

{
  "mappings": "AAAAA,SAASC,cAAc,WAAWC, ...",
  "sources": ["src/script.ts"],
  "sourcesContent": ["document.querySelector('button')..."],
  "names": ["document","querySelector", ...],
  "version": 3,
  "file": "example.min.js.map"
}

如需了解每个字段,您可以阅读源映射规范源映射详解

源映射最重要的部分是 mappings 字段。它使用 VLQ base 64 编码字符串将编译文件中的行和位置映射到相应的原始文件。您可以使用 source-map-visualizationSource Map Visualization 等源代码映射可视化工具查看此映射。

源代码映射可视化。
上一个代码示例的可视化结果,由可视化程序生成。

左侧的生成列显示压缩内容,原始列显示原始来源。

可视化工具会为 original 列中的每行分配颜色代码,并在 generated 列中显示相应的代码。

映射部分显示了代码的解码映射。例如,条目 65 -> 2:2 表示:

  • 生成的代码:const 一词位于压缩内容的 65 位。
  • 原始代码:在原始内容中,单词 const 从第 2 行和第 2 列开始。
映射条目。
映射可视化结果,重点显示 65 -> 2:2 条目。

这样一来,开发者便可以快速确定缩减版代码与原始代码之间的关系,从而使调试过程变得更加顺畅。

浏览器开发者工具会应用这些源代码映射,以帮助您在浏览器中快速查明调试问题。

应用源映射的开发者工具。
示例:浏览器开发者工具如何应用源代码映射并显示文件之间的映射。

源代码映射扩展

源映射支持以 x_ 前缀开头的自定义扩展字段。例如,Chrome DevTools 提出的 x_google_ignoreList 扩展字段。如需详细了解这些扩展程序如何帮助您专注于代码,请参阅 x_google_ignoreList

来源映射的缺点

遗憾的是,来源映射并非总能如期完成。在第一个示例中,变量 greet 在构建过程中被优化掉了,即使其值直接嵌入到最终的字符串输出中也是如此。

变量问候语未映射。
映射中缺少原始代码中的 greet 变量。

在这种情况下,当您调试代码时,开发者工具可能无法推断和显示实际值。此类错误可能会使代码监控和分析变得更加困难。

变量 greet 未定义。
开发者工具找不到 greet 的值。

这是需要在源映射的设计中解决的问题。一个可能的解决方案是在源映射中添加作用域信息,就像其他编程语言处理调试信息一样。

不过,这需要整个生态系统协同工作,以改进源映射规范和实现。如需了解我们如何持续改进源代码映射以提高可调试性,请参阅 GitHub 上针对 Source Maps v4 的提案。