使用源代码映射改善 Web 调试体验。
今天,我们要讨论的是源代码映射,它是现代 Web 开发中的一种重要工具,可大幅简化调试工作。在本文中,我们将探讨源映射的基础知识、如何生成源映射,以及源映射如何提升调试体验。
对源代码映射的需求
过去,我们只使用 HTML、CSS 和 JavaScript 构建了 Web 应用,并将相同的文件部署到网络中。
然而,当我们当前构建更复杂的 Web 应用时,您的开发工作流可能会涉及使用各种工具。例如:
- 模板语言和 HTML 预处理器:Pug、Nunjucks、Markdown。
- CSS 预处理器:SCSS、LESS、PostCSS。
- JavaScript 框架:Angular、React、Vue、Svelte。
- JavaScript 元框架:Next.js、Nuxt、Astro。
- 高级编程语言:TypeScript、Dart、CoffeeScript。
- 等等。这个清单可以继续添加!
这些工具需要一个构建流程,以将您的代码转译为浏览器可以理解的标准 HTML、JavaScript 和 CSS。此外,为了优化性能,常见做法是压缩(例如,使用 Terser 缩减和破坏 JavaScript)和合并这些文件,从而缩减文件的大小并提高它们对 Web 的使用效率。
例如,使用构建工具,我们可以将以下 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 和来源映射可视化)直观呈现此映射。
左侧的已生成列会显示压缩内容,原始列会显示原始来源。
可视化工具以颜色标识 original 列中的每一行,以及 generated 列中的相应代码。
mapping 部分会显示代码的解码映射。例如,条目 65-> 2:2
表示:
- 生成代码:单词
const
从压缩内容中的第 65 位开始。 - 原始代码:文字
const
从原始内容中的第 2 行第 2 列开始。
这样一来,开发者便可以快速确定经过缩减的代码与原始代码之间的关系,从而使调试过程更加顺畅。
浏览器开发者工具应用这些源映射,有助于您直接在浏览器中更快地查明调试问题。
此图片显示了浏览器开发者工具如何应用源代码映射,并显示了文件之间的映射。
来源映射扩展
来源映射支持扩展。扩展是以 x_
命名惯例开头的自定义字段。例如,Chrome 开发者工具建议的 x_google_ignoreList
扩展程序字段。请查看 x_google_ignoreList,详细了解这些扩展程序如何帮助您专心编写代码。
有待完善
在我们的示例中,变量 greet
在构建流程中已经过优化。该值直接嵌入到最终的字符串输出中。
在这种情况下,当您调试代码时,开发者工具可能无法推断和显示实际值。这不仅是浏览器开发者工具面临的挑战。这也使代码监控和分析变得更加困难。
这当然是一个可解决的问题。其中一种方法是在源映射中添加作用域信息,就像其他编程语言处理其调试信息一样。
不过,这需要整个生态系统通力协作,以改进源代码映射规范和实现。有一个积极讨论会讨论如何使用源代码映射提高可调试性。
我们期待改进源代码映射,让调试工作更加轻松!