源代码映射是现代 Web 开发中的重要工具,可以大幅简化调试工作。本页面介绍了有关源代码映射的基础知识、其生成方式,以及源代码映射如何改善调试体验。
对源代码映射的需求
早期的 Web 应用以较低的复杂性构建。开发者将 HTML、CSS 和 JavaScript 文件直接部署到了网络。
更现代、更复杂的 Web 应用可能需要在开发工作流中使用各种工具。例如:
- 模板语言和 HTML 预处理器:Pug、Nunjucks、Markdown。
- CSS 预处理器:SCSS、LESS、PostCSS。
- JavaScript 框架:Angular、React、Vue、Svelte。
- JavaScript 元框架:Next.js、Nuxt、Astro。
- 高级编程语言:TypeScript、Dart、CoffeeScript。
![各种工具的简要概览。](https://web.developers.google.cn/static/articles/source-maps/image/a-brief-overview-various-769879995ff7c.png?authuser=8&hl=hu)
这些工具需要一个构建流程,以将代码转译为浏览器可以理解的标准 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.map
和 styles.css.map
)。大多数构建工具都可以生成它们,包括 Vite、webpack、Rollup、Parcel 和 esbuild。
有些工具默认包含源代码映射。而其他架构可能需要额外配置才能生成:
/* 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-visualization 或来源映射可视化)查看此映射。
![源映射可视化。](https://web.developers.google.cn/static/articles/source-maps/image/a-source-map-visualizatio-4866bc351df59.png?authuser=8&hl=hu)
左侧的已生成列显示压缩的内容,原始列显示原始来源。
可视化工具会对 original 列中的每一行及其对应的代码在 generated 列中进行颜色编码。
mapping 部分会显示代码的解码映射。例如,条目 65 -> 2:2
表示:
- 生成代码:单词
const
从压缩内容中的第 65 位开始。 - 原始代码:文字
const
从原始内容中的第 2 行第 2 列开始。
![映射条目。](https://web.developers.google.cn/static/articles/source-maps/image/mapping-entry-dc9df15808405.png?authuser=8&hl=hu)
65 -> 2:2
条目。这样一来,开发者便可以快速确定缩减后的代码与原始代码之间的关系,从而使调试过程更加顺畅。
浏览器开发者工具会应用这些源代码映射,以帮助您在浏览器中快速找出调试问题。
![应用源代码映射的开发者工具。](https://web.developers.google.cn/static/articles/source-maps/image/developer-tools-applies-ea66f57e838eb.png?authuser=8&hl=hu)
来源映射扩展
来源映射支持以 x_
前缀开头的自定义扩展字段。例如,Chrome DevTools 建议的 x_google_ignoreList
扩展字段。如需详细了解这些扩展程序如何帮助您专心处理代码,请参阅 x_google_ignoreList。
来源映射的缺点
遗憾的是,源映射并不总是达到您所需的完整。在第一个示例中,虽然变量 greet
的值已直接嵌入到最终的字符串输出中,但在构建过程中优化了该变量。
![未映射变量问候语。](https://web.developers.google.cn/static/articles/source-maps/image/variaqble-greet-is-map-a93a440f3c171.png?authuser=8&hl=hu)
greet
变量。在这种情况下,当您调试代码时,开发者工具可能无法推断并显示实际值。此类错误可能会加大代码监控和分析难度。
![未定义变量问候语。](https://web.developers.google.cn/static/articles/source-maps/image/variable-greet-is-undefin-19fcbf9a06a26.png?authuser=8&hl=hu)
greet
的值。这是需要在源映射的设计中解决的问题。一种可能的解决方案是像其他编程语言处理其调试信息一样,在源映射中包含作用域信息。
不过,这需要整个生态系统通力协作,以改进源代码映射规范和实现。如需关注使用源代码映射提高可调试性的持续性,请参阅 GitHub 上的 Source Maps v4 提案。