部署前端之前,開發人員通常會對代碼進行打包壓縮,這樣可以減少代碼大小,從而有效提高訪問速度。然而,壓縮代碼的報錯資訊是很難Debug的,因為它的行號和列號已經失真。這時就需要Source Map來還原真實的出錯位置了。
為啥變換代碼?
前端代碼越來越複雜的情況下,開發人員通常會使用webpack、UglifyJS2等工具對代碼進行打包變換,這樣可以減少代碼大小,有效提高訪問速度。關於變換代碼的原因,這裡不妨引用一下大神阮一峰的JavaScript Source Map 詳解:
- 壓縮,減小體積。比如jQuery 1.9的源碼,壓縮前是252KB,壓縮後是32KB。
- 多個檔案合并,減少HTTP請求數。
- 其他語言編譯成JavaScript。最常見的例子就是CoffeeScript。
如何變換代碼?
下面是一個簡單的“hello World”程式hello.js
function sayHello(){ var name = "Fundebug"; var greeting = "Hello, " + Name; console.log(greeting);}sayHello();
使用UglifyJS2對原始碼進行壓縮變換:
uglifyjs hello.js \ -m toplevel=true \ -c unused=true,collapse_vars=true \ -o hello.min.js
壓縮後的代碼hello.min.js
function o(){var o="Hello, "+Name;console.log(o)}o();
為啥需要Source Map?
使用Firefox執行hello.js的報錯資訊是這樣:
ReferenceError: Name is not defined sayHello file:///Users/fundebug/sourcemap-tutorial/hello.js:4:9 <匿名> file:///Users/fundebug/sourcemap-tutorial/hello.js:8:1
而hello.min.js的報錯資訊是這樣:
ReferenceError: Name is not defined o file:///Users/fundebug/sourcemap-tutorial/hello.min.js:1:18 <匿名> file:///Users/fundebug/sourcemap-tutorial/hello.min.js:1:59
對比壓縮前後的出錯資訊,我們會發現,錯誤行號和列號已經失真,且函數名也經過了變換。而對於真實的前端項目,開發人員會將數十個源檔案壓縮為一個檔案,這時,錯誤的列號可能多達數千,且出錯的真實檔案名稱也是很難確定的,這樣的話,壓縮代碼的報錯資訊是很難Debug的。
而Source Map則可以用於還原真實的出錯位置,協助開發人員更快的Debug。
什麼是Source Map?
使用UglifyJS2時指定source-map選項即可產生Source Map:
uglifyjs hello.js \ -m toplevel=true \ -c unused=true,collapse_vars=true \ --source-map hello.min.js.map \ --source-map-include-sources \ --source-map-root \ -o hello.min.js
各種主流前端任務管理工具,打包工具都支援產生Source Map,具體可以查看產生Source Map - Fundebug文檔。
產生的hello.min.js多了sourceMappingURL,表示Source Map檔案的位置。
function o(){var o="Hello, "+Name;console.log(o)}o();//# sourceMappingURL=hello.min.js.map
產生的Source Map為hello.min.js.map:
{ "version": 3, "sources": ["hello.js"], "names": ["sayHello", "greeting", "Name", "console", "log"], "mappings": "AAAA,QAASA,KAEL,GACIC,GAAW,UAAYC,IAC3BC,SAAQC,IAAIH,GAGhBD", "file": "hello.min.js", "sourceRoot": "", "sourcesContent": ["function sayHello()\n{\n var name = \"Fundebug\";\n var greeting = \"Hello, \" + Name;\n console.log(greeting);\n}\n\nsayHello();\n"]}
由hello.min.js.map可知,Source Map是一個JSON檔案,而它包含了代碼轉換前後的位置資訊。也就是說,給定一個轉換之後的壓縮代碼的位置,就可以通過Source Map擷取轉換之前的代碼位置,反過來也一樣。Source Map各個屬性的含義如下:
- version:Source Map的版本號碼。
- sources:轉換前的檔案清單。
- names:轉換前的所有變數名和屬性名稱。
- mappings:記錄位置資訊的字串,經過編碼。
- file:(可選)轉換後的檔案名稱。
- sourceRoot:(可選)轉換前的檔案所在的目錄。如果與轉換前的檔案在同一目錄,該項為空白。
- sourcesContent:(可選)轉換前的檔案內容列表,與sources列表依次對應。
Source Map真正神奇之處在於mappings屬性,它記錄了位置是如何對應的。JavaScript Source Map 詳解已經有很好的解釋,這裡不再贅述。
怎樣使用Source Map?
主流瀏覽器均支援Source Map功能,不過Chrome與Firefox需要一些簡單的配置,具體步驟請參考How to enable source maps。下面以MacBook上的Chrome瀏覽器為例,介紹一下配置方法:
1. 開啟
開發人員工具
使用快速鍵option + command + i;或者在功能表列選擇視圖->開發人員->開發人員工具
2. 開啟設定
使用快速鍵fn + F1;或者點擊右上方的三個點的表徵圖,選擇Settings
3. 開啟Source Map
在Sources中,選中Enable JavaScript source maps
為了測試,我寫了一個簡單的HTML檔案hello.min.html
<head> <script type="text/javascript" src="hello.min.js"></script></head>
使用Chrome開啟hello.min.html,在控制台看到的錯誤如下:
Uncaught ReferenceError: Name is not defined at o (hello.min.js:1) at hello.min.js:1
報錯的檔案仍然為hello.min.js,需要重新整理一下Source Map才有作用:
Uncaught ReferenceError: Name is not defined at o (hello.js:4) at hello.js:8
注意,Chrome的報錯資訊沒有列號,因此4為錯誤的行號。Chrome不僅可以通過Source Map還原真實的出錯位置,還可以根據Source Map的sourcesContent還原出錯的原始碼。點擊出錯位置,即可跳轉到源碼,這樣Debug將非常方便。
參考連結
- JavaScript Source Map 詳解
- Source Map Revision 3 Proposal
- How to enable source maps
著作權聲明:
時請註明作者Fundebug以及本文地址: