[譯] 什麼是 SourceMap
原文鏈接: https://web.dev/source-maps/
使用 SourceMap 來提升 web 調試體驗。
今天,我們要討論的是 SourceMap,這是現代 Web 開發中至關重要的工具,它能夠顯著地簡化調試工作。在本文中,我們將探討 SourceMap 的基礎知識,包括它們的生成方式以及它們如何改善調試體驗。
SourceMap 的必要性
以前,我們使用純 HTML、CSS 和 JavaScript 構建 Web 應用程序,並將相同的文件部署到 Web 上。
然而,隨着現代 Web 應用程序日益複雜,你的開發工作流可能用到了各種工具。例如:
-
模板語言和 HTML 預處理器:Pug、Nunjucks、Markdown。
-
CSS 預處理器:SCSS、LESS、PostCSS。
-
JavaScript 框架:Angular、React、Vue、Svelte。
-
JavaScript Meta 框架:Next.js、Nuxt、Astro。
-
高級編程語言:TypeScript、Dart、CoffeeScript。
等等。
這些工具會將代碼轉換爲標準的 HTML、JavaScript 和 CSS 後給瀏覽器運行。此外,爲了優化性能,通常會對這些文件進行壓縮(例如使用 Terser 進行 JavaScript 的壓縮和混淆),並將它們合併在一起,減小它們的大小,使其在 Web 傳輸上更加高效。
例如,使用構建工具,我們可以將以下 TypeScript 文件轉換爲一行 JavaScript 並進行壓縮。你可以在我的 GitHub 庫 (https://github.com/jecfish/parcel-demo) 中嘗試這個演示。
/* 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)}));
但是,這種優化可能會使調試工作更困難。將所有內容都放在一行中並使用更短的變量名的壓縮代碼可能會使問題更難定位。而 SourceMap 的用途,就是將編譯後的代碼映射回原始代碼。
生成 SourceMap
SourceMap 是以 .map
結尾的文件,比如 example.min.js.map
和 styles.css.map
。它們可以被大多數構建工具生成,例如 Vite、webpack、Rollup、Parcel、esbuild 等等。
一些工具默認包含 SourceMap,而其他工具可能需要額外的配置來生成。
/* 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
}
})
理解 SourceMap
這些 SourceMap 文件包含有關編譯代碼如何映射到原始代碼的重要信息,使開發人員可以輕鬆調試。以下是 SourceMap 的示例。
{
"mappings": "AAAAA,SAASC,cAAc,WAAWC, ...",
"sources": ["src/script.ts"],
"sourcesContent": ["document.querySelector('button')..."],
"names": ["document","querySelector", ...],
"version": 3,
"file": "example.min.js.map"
}
要理解每個字段,可以閱讀 SourceMap 規範或者這篇經典文章《SourceMap 的剖析》。
SourceMap 最重要的方面是 mappings 字段。它使用 Base64 VLQ 編碼字符串將編譯文件中的行和位置映射到相應的原始文件。這個映射可以使用 SourceMap 通過工具(例如 source-map-visualization 和 Source Map Visualization)可視化。
上圖左邊顯示壓縮後的內容,右邊顯示源代碼。
可視化工具爲原始列中的每一行和其在生成列中對應的代碼着色。
mappings 部分顯示了代碼的解碼映射。例如,條目 65->2:2 表示:
-
生成的代碼:單詞 const 在壓縮內容的位置 65 開始。
-
源代碼:單詞 const 在原始內容中的第 2 行第 2 列開始。
通過這種方式,開發人員可以快速識別壓縮後的代碼與源代碼之間的關係,使得調試過程更加順暢。
瀏覽器開發者工具應用這些源映射,幫助你更快地定位調試問題,直接在瀏覽器中進行調試。
該圖展示了瀏覽器開發者工具如何應用源映射,並顯示文件之間的映射關係。
SourceMap 擴展
SourceMap 也支持擴展。擴展是以 x_
命名約定開頭的自定義字段。例如由 Chrome DevTools 提出的 x_google_ignoreList 擴展字段。請查看 x_google_ignoreList(https://developer.chrome.com/articles/x-google-ignore-list/) 來了解這些擴展如何幫助你專注於代碼。
不夠完美
在我們的例子中,變量 greet 在構建過程中被優化掉了。該值直接嵌入到最終的字符串輸出中。
在這種情況下調試代碼時,開發者工具可能無法推斷並顯示實際值。它不僅是瀏覽器開發者工具的挑戰,也使得代碼調試和分析變得更加困難。
當然,這是可以解決的問題。一種方法是在源映射中包含作用域信息,就像其他編程語言使用它們的調試信息一樣。
總之,改進源映射規範和實現仍需共同努力。目前已經有一場活躍的關於如何通過源映射來提高調試能力的討論 (https://github.com/source-map/source-map-rfc/issues/12)。
我們期待着改進 SourceMap,讓調試變得更加輕鬆!
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/onAw4_DfYrt7B5QPXCBgig