隨着前端代碼越開越複雜的狀況下,開發者一般會使用webpack、UglifyJS2等工具對代碼進行打包變換,這樣能夠減小代碼大小,有效提升訪問速度,同時還可以有效的保護源代碼不被別人獲取。javascript
正常代碼hello.js:
function sayHello() {
var name = "Fundebug";
var greeting = "Hello, " + Name;
console.log(greeting);
}
sayHello();
>>>>>>>>>>使用UglifyJS2對源碼進行壓縮 start>>>>>>>>>>>>
uglifyjs hello.js \
-m toplevel=true \
-c unused=true,collapse_vars=true \
-o hello.min.js
>>>>>>>>>>使用UglifyJS2對源碼進行壓縮 end>>>>>>>>>>>>
壓縮後的代碼hello.min.js:
function o(){var o="Hello, "+Name;console.log(o)}o();
複製代碼
然而壓縮代碼的報錯信息是很難Debug的,由於它的行號和列號已經失真。
執行hello.js的報錯信息,咱們可以直接定位到出錯的位置。 html
而執行壓縮後的hello.min.js的報錯信息,
前端
對比壓縮先後的出錯信息,咱們會發現,錯誤行號和列號已經失真,且函數名也通過了變換。而對於真實的前端項目,開發者會將數十個源文件壓縮爲一個文件,這時,錯誤的列號可能多達數千,且出錯的真實文件名也是很難肯定的,這樣的話,壓縮代碼的報錯信息是很難Debug的。
而Source Map則能夠用於還原真實的出錯位置,幫助開發者更快的Debug。java
簡單說,Source map就是一個信息文件,裏面儲存着位置信息。也就是說,轉換後的代碼的每個位置,所對應的轉換前的位置。
有了它,出錯的時候,除錯工具將直接顯示原始代碼,而不是轉換後的代碼。這無疑給開發者帶來了很大方便。webpack
目前各類主流前端任務管理工具,打包工具都支持生成Source Map,具體能夠查看生成SourceMap。
使用UglifyJS2時指定source-map選項便可生成Source Map:web
>>>>>>>>>>使用UglifyJS2生成SourceMap start>>>>>>>>>>>>
uglifyjs hello.js \
-m toplevel=true \
-c unused=true,collapse_vars=true \
--source-map url='hello.min.js.map' \
-o hello.min.js
>>>>>>>>>>使用UglifyJS2生成SourceMap end>>>>>>>>>>>>
添加了SourceMap的hello.min.js:
function o(){var o="Hello, "+Name;console.log(o)}o();
//# sourceMappingURL=hello.min.js.map
生成的SourceMap文件hello.min.js.map:
{
"version": 3,
"sources": [
"hello.js"
],
"names": [
"sayHello",
"greeting",
"Name",
"console",
"log"
],
"mappings": "AAAA,SAASA,IACL,IACIC,EAAW,UAAYC,KAC3BC,QAAQC,IAAIH,GAEhBD"
}
複製代碼
由hello.min.js.map可知,Source Map是一個JSON文件,而它包含了代碼轉換先後的位置信息。也就是說,給定一個轉換以後的壓縮代碼的位置,就能夠經過Source Map獲取轉換以前的代碼位置,反過來也同樣。Source Map各個屬性的含義以下:數組
Source Map真正神奇之處在於mappings屬性,它記錄了位置是如何對應的。bash
sourcemap的mappings屬性的值是將位置映射關係採用Base 64 VLQ編碼後生成的字符串。
mappings屬性值包含的信息能夠按照下面的方式進行拆分:app
注意:每組VLQ編碼字段有0~N個VLQ編碼字符組成,如 qC | A | A | U | H。
例如「AAAAA」表示的就是:該位置在轉換後代碼的第0列,對應sources屬性中第0個文件,屬於轉換前代碼的第0行第0列,對應names屬性中的第0個變量。函數
VLQ編碼最先用於MIDI文件,後來被多種格式採用。它的特色就是能夠很是精簡地表示很大的數值。它規定,每一個字符使用6個兩進制位。
以下圖所示:
上面看的可能比較迷糊,咱們看一個例子,如何對數值137進行VLQ編碼:
注意:轉換爲二進制碼以後,是從低位到高位的進行Base64轉換。
下圖爲base64的映射表:
咱們瞭解完了VLQ的編解碼以後,咱們將開頭生成的SourceMap文件的mappings屬性分析一下看看是否可以一一對應。
開頭生成的SourceMap文件的mappings屬性爲:
"mappings": "AAAA,SAASA,IACL,IACIC,EAAW,UAAYC,KAC3BC,QAAQC,IAAIH,GAEhBD"
複製代碼
咱們使用Base64VLQ在線編解碼網站把內容解碼一下:
[0,0,0,0], [9,0,0,9,0], [4,0,1,-5], [4,0,1,4,1], [2,0,0,11],
[10,0,0,12,1], [5,0,1,-27,1], [8,0,0,8,1], [4,0,0,4,-3], [3,0,2,-16,-1]
複製代碼
注意:mappings的位置值都是相對位置,每一個值都是相對於前一個位置值的。(當文件內容巨大時,精簡後的編碼也有可能會由於數字位數的增長而變得很長,同時,處理較大數字老是不如處理較小數字容易和方便。因而mappings中的位置記錄是記錄的這些位置的相對值。)
所以每個位置值加上前一個位置值能夠獲得:
[0,0,0,0], [9,0,0,9,0], [13,0,1,4], [17,0,2,8,1], [19,0,2,19],
[29,0,2,31,2], [34,0,3,4,3], [42,0,3,12,4], [46,0,3,16,1], [49,0,5,0,0]
複製代碼
整理一下能夠得出以下結果:
(a,b)=>name(m,n)表示生成代碼中的a行b列對應原始代碼中的m行n列的位置,而且在原始代碼中這個位置的變量名是name。
(0,0)=>(0,0)
(0,9)=>sayHello(0,9)
(0,13)=>(1,4)
(0,17)=>greeting(2,8)
(0,19)=>(2,19)
(0,29)=>Name(2,31)
(0,34)=>console(3,4)
(0,42)=>log(3,12)
(0,46)=>greeting(3,16)
(0,49)=>(5,0)
複製代碼