sourceMap是啥

爲啥用sourceMap

這幾天在搞前端錯誤日誌,做過線上發佈的都知道,我們發佈到生產環境的代碼,一般都有如下步驟:

  • 壓縮混淆,減小體積
  • 多個文件合併,減少HTTP請求數
  • 通過編譯或者轉譯,將其他語言編譯成JavaScript

這三個步驟,都使得實際運行的代碼不同於開發代碼,不管是 debug 還是捕獲線上的報錯,都會變得困難重重。

解決這個問題的方法,就是使用sourceMap

啥是sourceMap

簡單說,sourceMap就是一個文件,裏面儲存着位置信息。

仔細點說,這個文件裏保存的,是轉換後代碼的位置,和對應的轉換前的位置。

有了它,出錯的時候,通過斷點工具可以直接顯示原始代碼,而不是轉換後的代碼。

sourceMap長啥樣

通過webpack等工具,我們可以使用 sourceMap,這裏不細說配置方法,可以看這裏

sourceMap是一個map文件,與源碼在同一個目錄下。

在壓縮代碼的最後一行,會有這樣的一個引用:

//# sourceMappingURL=app.js.map

指向的就是我們的map文件。

sourceMap的格式如下:

{
    version : 3, //SourceMap的版本,目前爲3
    sources: ["foo.js", "bar.js"], //轉換前的文件,該項是一個數組,表示可能存在多個文件合併
    names: ["src", "maps", "are", "fun"], //轉換前的所有變量名和屬性名
    mappings: "AACvB,gBAAgB,EAAE;AAClB;", //記錄位置信息的字符串
    file: "out.js", //轉換後的文件名
    sourcesContent: " \t// The module cache\n", //轉換後的代碼
    sourceRoot : "" //轉換前的文件所在的目錄。如果與轉換前的文件在同一目錄,該項爲空
}

其他的都很好解釋,我們詳細說一下mappings屬性。

mappings

"AACvB,gBAAgB,EAAE;AAClB;"爲例:

  • 每個分號對應轉換後源碼的一行;
  • 每個逗號對應轉換後源碼的一個位置;
  • AACvB代表該位置轉換前的源碼位置,以VLQ編碼表示;

位置對應的原理

位置關係的保存經歷了諸多步驟和優化,這個不詳細說了,想看的可以看這裏,我們只說最後的結果。

在每個位置中:

  • 第一位,表示這個位置在【轉換後代碼】的第幾列。
  • 第二位,表示這個位置屬於【sources屬性】中的哪一個文件。
  • 第三位,表示這個位置屬於【轉換前代碼】的第幾行。
  • 第四位,表示這個位置屬於【轉換前代碼】的第幾列。
  • 第五位,表示這個位置屬於【names屬性】的哪一個變量。

舉例

假設現在有a.js,內容爲feel the force,處理後爲b.js,內容爲the force feel

the爲例,它在輸出中的位置是(0,0),a.jssources的第1個(這裏只是舉例),輸入中的位置是(0,5),thenames的第2個(這裏只是舉例)。

那麼映射關係爲:
0 1 0 5 2

最後將 01052 表示爲 Base64 VLQ 即可。

說明:

  • 所有的值都是以0作爲基數
  • 第五位不是必需的,如果該位置沒有對應names屬性中的變量,可以省略第五位
  • 每一位都採用VLQ編碼表示,由於VLQ編碼是可變長的,所以每一位可以由多個字符構成
  • 爲什麼不保存轉換後代碼的行號,因爲我們輸出的文件總是一行,這樣輸出的行號就可以省略,因爲都是0,沒必要寫出來
  • 對於輸出後的位置來說,到後邊會發現它的列號特別大,爲了避免這個問題,採用相對位置進行描述

相對位置是啥呢,看示意圖:

第一次記錄的輸入位置和輸出位置是絕對的,往後的輸入位置和輸出位置都是相對上一次的位置移動了多少,例如the的輸出位置爲(0,-10),因爲thefeel的左邊數10下才能到這個位置。

VLQ編碼

VLQVariable-length quantity 的縮寫,是一種通用的、使用任意位數的二進制來表示一個任意大的數字的一種編碼方式。這種編碼最早用於MIDI文件,後來被多種格式採用,它的特點就是可以非常精簡地表示很大的數值,用來節省空間。

這種編碼需要用最高位表示連續性,如果是1,代表這組字節後面的一組字節也屬於同一個數;如果是0,表示該數值到這就結束了。

這樣乾巴巴說不太容易懂,還是舉個栗子說明一下吧。

如何對數值137進行VLQ編碼:

步驟 結果
將137改寫成二進制形式 10001001
七位一組做分組,不足的補0 0000001 0001001
最後一組開頭補0,其餘補1 10000001 00001001

所以,137的VLQ編碼形式爲10000001 00001001

Base64 VLQ

與一般的VLQ的區別:

  • 一個Base64字符只能表示 6bit(2^6)的數據
  • Base64 VLQ需要能夠表示負數,於是用最後一位來作爲符號標誌位。
  • 由於只能用6位進行存儲,而第一位表示是否連續的標誌,最後一位表示正數/負數。中間只有4位,因此一個單元表示的範圍爲[-15,15],如果超過了就要用連續標識位了。

表示正負的方式:

  • 如果這組數是某個數值的VLQ編碼的第一組字節,那它的最後一位代表"符號",0爲正,1爲負;
  • 如果不是,這個位沒有特殊含義,被算作數值的一部分。

我們再來舉個栗子說明下使用方法。

如何對數值137進行Base64 VLQ編碼:

步驟 結果
將137改寫成二進制形式 10001001
127是正數,末位補0 100010010
五位一組做分組,不足的補0 01000 10010
將組倒序排序 10010 01000
最後一組開頭補0,其餘補1 110010 001000
轉64進制 y和I

所以 137 通過Base64 VLQ表示爲yl

可以看出:

  • Base64 VLQ中,編碼順序是從低位到高位
  • 而在VLQ中,編碼順序是從高位到低位

參考文章

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章