您是否曾希望能在不影響效能的情況下,將用戶端程式碼保持在可讀性,並進一步偵錯) 嗎?您現在可以盡情使用來源地圖的強大功能。
來源對應是一種將合併/壓縮檔案對應至未建構狀態的方法。您在建置實際工作環境、壓縮及合併 JavaScript 檔案時,都會產生來源對應檔,用於保存原始檔案的資訊。當您在產生的 JavaScript 中查詢特定的行號和欄號時,可以在傳回原始位置的來源對應中查詢。開發人員工具 (目前採用 WebKit 夜間版本、Google Chrome 或 Firefox 23 以上版本) 可以自動剖析來源對應,讓它看起來像正在執行未合併且未合併的檔案一樣。
在這個示範中,您可以在包含生成來源的文字區域按一下滑鼠右鍵,選取「取得原始位置」會傳入產生的行和欄編號,查詢來源對應,並傳回原始程式碼中的位置。請確認主控台已開啟,查看輸出內容。
真實世界
查看以下的「來源地圖」實際實作項目之前,請確認您已在 Chrome Canary 或 WebKit 晚上啟用來源地圖功能 (方法是按一下開發人員工具面板中的設定齒輪圖示,然後勾選 [啟用來源對應])。如果有需要 SQL 指令的分析工作負載 則 BigQuery 可能是最佳選擇
Firefox 23+ 的內建工具會預設啟用來源對應功能。
為何要關注來源對應?
目前只有未壓縮/合併的 JavaScript 與經過壓縮/未合併的 JavaScript 之間需要來源對應,但包括 CoffeeScript 等編譯至 JavaScript 語言,甚至新增對 SASS 或 LESS 等 CSS 預先處理器的支援,目前來源對應顯而易見。
未來我們只要使用來源地圖,就能直接使用幾乎任何語言,就像瀏覽器原生支援的語言一樣:
- CoffeeScript
- ECMAScript 6 及以上版本
- 銷售/較少
- 幾乎所有可編譯為 JavaScript 的語言
請查看以下螢幕側錄中的 CoffeeScript 偵錯在 Firefox 控制台中的實驗版本:
Google Web Toolkit (GWT) 最近新增了來源地圖支援。 GWT 團隊的 Ray Cromwell 製作了精彩的螢幕側錄影片,呈現來源地圖的即時支援。
另一個例子是使用 Google 的 Traceur 程式庫,可用來編寫 ES6 (ECMAScript 6 或 Next),並編譯為 ES3 相容程式碼。Traceur 編譯器也會產生來源對應。您可以在這個示範中瞭解 ES6 特徵和類別的使用過程如何獲得瀏覽器原生支援,這都要歸功於來源對應。
示範中的文字區域也可讓您編寫 ES6,系統會即時編譯,然後產生來源對應和同等的 ES3 程式碼。
來源對應的運作方式為何?
目前唯一支援產生來源地圖的 JavaScript 編譯器/最小值是 Closure 編譯器。(稍後會說明使用方式)。合併及壓縮 JavaScript 之後,連同程式碼一起存在來源對應檔案。
目前 Closure 編譯器在結尾不會加上特殊註解,這是要求來源對應可用的瀏覽器開發工具時所需的特殊註解:
//# sourceMappingURL=/path/to/file.js.map
這可讓開發人員工具將呼叫對應到原始來源檔案中位置。先前的註解空白處是 //@
,但因該問題和 IE 條件式編譯註解而出現一些問題,所以做出了決定,將註解變更為 //#
。目前 Chrome Canary、WebKit Nightly 和 Firefox 24 以上版本均支援新的註解區塊。這項語法變更也會影響 sourceURL。
如果您不喜歡這個奇怪的留言,可以在編譯後的 JavaScript 檔案中設定特殊標頭:
X-SourceMap: /path/to/file.js.map
就像註解一樣,可讓您的來源地圖用戶端尋找與 JavaScript 檔案相關聯的來源地圖。這個標頭也會說明在不支援單行註解的語言中參照來源對應的問題。
只有在已啟用來源對應且開發工具開啟時,才會下載來源對應檔案。您還需要上傳原始檔案,開發工具才能參照及顯示必要檔案。
如何產生來源對應?
您必須使用 Closure 編譯器,才能壓縮、串連並產生 JavaScript 檔案的來源對應。指令如下:
java -jar compiler.jar \
--js script.js \
--create_source_map ./script-min.js.map \
--source_map_format=V3 \
--js_output_file script-min.js
兩個重要的指令旗標是 --create_source_map
和 --source_map_format
。這是必要版本,因為預設版本為 V2,而我們只想搭配 V3。
來源對應的剖析
為進一步瞭解來源對應,我們會取用一個由 Closure 編譯器產生的來源地圖檔案範例,並進一步說明「對應」如何部分以下範例是與 V3 規格示例中的細微差異。
{
version : 3,
file: "out.js",
sourceRoot : "",
sources: ["foo.js", "bar.js"],
names: ["src", "maps", "are", "fun"],
mappings: "AAgBC,SAAQ,CAAEA"
}
如上圖所示,來源對應是物件常值,包含大量煙火資訊:
- 來源對應的版本號碼
- 產生的程式碼檔案名稱 (完成壓縮/合併的實際檔案)
- sourceRoot 可讓您在資料夾結構前面加上來源,這也是一種節省空間的技巧
- source 會列出所有已合併的檔案名稱
- 名稱包含整個程式碼中的所有變數/方法名稱。
- 最後,Mappings 屬性是使用 Base64 VLQ 值產生神奇效果的地方。立即儲存實際空間。
Base64 VLQ 並保持來源地圖的尺寸
最初,來源對應規格輸出所有對應關係後,產生了相當詳細的輸出內容,因此來源對應造成來源對應大小,是產生程式碼大小的 10 倍。第二版減少了 50% 左右,版本 3 又減少了 50%。因此,133 kB 的檔案會得到約 300 KB 的來源對應。
那麼他們如何縮減尺寸,同時保持複雜的對應?
VLQ (可變長度數量) 會與將值編碼為 Base64 值搭配使用。對應屬性是超大型字串。在這個字串中,分號 (;) 代表產生的檔案中的行數。每行都有半形逗號 (,),代表該行中的各個區隔。這些區隔在可變長度欄位中各為 1、4 或 5。有些則可能較長,但內含連續位元。每個區隔都是以前一個區隔為基礎建立而成,這種做法有助於縮減檔案大小,因為每個位元均與先前區隔相關。
如上所述,每個區隔的長度可變為 1、4 或 5。此圖表被視為可變長度為 4,還有一個接續位元 (g)。我們會詳細介紹此區段,並顯示來源對應的運作方式。
上方顯示的值只是 Base64 解碼值,還需要經過處理才能取得真正的值。每個區隔的彙整依據通常有五項:
- 產生的資料欄
- 出現這個原始檔案的原始檔案
- 原始行號
- 原始資料欄
- (如果有的話) 原始名稱
不是每個區隔都有名稱、方法名稱或引數,因此整個區隔會在 4 到 5 個變數長度之間切換。上方區隔圖中的 g 值稱為接續位元,這可讓您在 Base64 VLQ 解碼階段中進一步最佳化。接續位元可讓您以區隔值為基礎,讓您不需儲存大量數字,因為這種巧妙的空間節省空間,以 midi 格式具有根基。
上述圖表 AAgBC
經過進一步處理後,就會傳回 0、0、32、16、1 - 32 是有助於建構下列值 16 的接續位元。在 Base64 中單純解碼的 B 是 1。因此使用的重要值是 0、0、16、1。這樣即可告訴我們,產生的檔案所對應的第 1 行 (行號以分號保存) 第 0 行對應至檔案 0 (0 陣列為 foo.js),第 16 行第 1 行。
為了顯示區隔解碼方式,我將參考 Mozilla 的來源對應 JavaScript 程式庫。您也可以查看 WebKit 開發人員工具原始碼對應程式碼 (同樣以 JavaScript 編寫)。
為了正確瞭解我們如何從 B 取得值 16,我們必須對位元運算子有基本的瞭解,以及規格如何用於來源對應。系統會透過使用位元 AND (&) 運算子比較數字 (32) 和 VLQ_CONTINUATION_BIT (二進位 100000 或 32),將上述數字 g 標記為接續位元。
32 & 32 = 32
// or
100000
|
|
V
100000
這會在兩者出現位置的每個位元位置傳回 1。所以,33 & 32
的 Base64 解碼值會傳回 32,因為它只會分享 32 位元的位置,如上圖所示。這樣一來,每個前一個接續位元的位元偏移值就會增加 5。在上述案例中,它的位移 5 次一次,因此左移 1 (B) 乘以 5。
1 <<../ 5 // 32
// Shift the bit by 5 spots
______
| |
V V
100001 = 100000 = 32
接著,這個值會由右移 (32) 一個位置,從 VLQ 簽署的值轉換。
32 >> 1 // 16
//or
100000
|
|
V
010000 = 16
簡單來說,就是 1 變成 16 了。這個過程看似複雜,但只要數據越大,就會更加合理。
潛在的 XSSI 問題
規格提到使用來源對應,因而導致的跨網站指令碼包含問題。為減緩此問題,建議您在來源對應的第一行前面加上「)]}
」刻意撤銷 JavaScript,避免系統擲回語法錯誤。WebKit 開發人員工具已能處理這個問題。
if (response.slice(0, 3) === ")]}") {
response = response.substring(response.indexOf('\n'));
}
如上所示,前三個字元已進行分割,檢查這些字元是否與規格中的語法錯誤相符;如果是的話,請移除第一個新行實體 (\n) 前的所有字元。
sourceURL
和 displayName
的實際應用:評估和匿名函式
雖然來源對應規格並未遵循下列兩個慣例,但可讓您在使用評估和匿名函式時,大幅簡化開發作業。
第一個輔助程式看起來與 //# sourceMappingURL
屬性非常類似,實際上是在來源對應 V3 規格中提及。將以下特殊註解納入程式碼,系統會評估這些註解,為評估作業命名,即可在開發人員工具中以更邏輯的名稱顯示評估項目。以下是使用 CoffeeScript 編譯器的簡易示範:
示範:透過 sourceURL 將 eval()
的程式碼顯示為指令碼
//# sourceURL=sqrt.coffee
另一個輔助程式可讓您使用匿名函式目前結構定義的 displayName
屬性,為匿名函式命名。剖析下列示範,瞭解 displayName
屬性的實際運作情形。
btns[0].addEventListener("click", function(e) {
var fn = function() {
console.log("You clicked button number: 1");
};
fn.displayName = "Anonymous function of button 1";
return fn();
}, false);
在開發人員工具中剖析程式碼時,系統會顯示 displayName
屬性,而非 (anonymous)
這類屬性。displayName 經常在水中死去,也收不到 Chrome。不過,您不會失去所有希望,因此我們建議採用 debugName 這個更好的提案。
截至本文撰寫既有命名規則為止,只有 Firefox 和 WebKit 瀏覽器支援。displayName
屬性只會在 WebKit 夜間顯示。
來集體結吧
CoffeeScript 目前正針對來源地圖支援加入相當長的討論。請查看問題,並新增對 CoffeeScript 編譯器產生來源對應的支援。這將能為 CoffeeScript 和忠實的追蹤者帶來莫大的助益。
此外,UglifyJS 還有一個來源地圖問題,建議您查看。
許多工具都能產生來源對應,包括 Cupscript 編譯器。我現在想到這個點子。
現有越多工具提供給我們,就能更完善地產生來源地圖。因此,在您最愛的開放原始碼專案裡,您可以尋求或新增來源地圖支援。
非常不完美
目前,來源對應還不支援監控運算式。問題在於,嘗試檢查目前執行環境中的引數或變數名稱不會傳回任何內容,因為它實際上不存在。這就需要某種反向對應,才能比對想檢查的引數/變數的實際名稱,以及已編譯 JavaScript 中的實際引數/變數名稱。
當然,這是一個可解決的問題,而且如果對來源地圖投注的關注,我們可以開始試用一些令人驚豔的功能,並且更加穩定。
問題
最近 jQuery 1.9 開始在離線 CDN 提供時對來源對應的支援。並在 jQuery 載入前使用 IE 條件編譯註解 (//@cc_on) 時,也指出發生錯誤錯誤。為了緩解這種情況,我們已修訂,將 sourceMappingURL 納入多行註解中。學習課程請不要使用條件式註解。
因此,我們已解決將語法變更為 //#
之後會發生的問題。
工具與資源
建議您參考下列額外資源和工具:
- Nick Fitzgerald 有一個支援來源地圖的 UglifyJS 分支
- Paul Ireland 提供了一個簡短的示範,展示來源地圖
- 查看 WebKit 變更會在何時捨棄
- 此變更組也進行了版面配置測試,以測試整篇報導
- Mozilla 推出一項錯誤,您應可在內建控制台中追蹤來源對應的狀態
- Conrad Irwin 寫了一篇超實用的來源地圖寶石,供 Ruby 使用者參考
- 如要進一步瞭解 eval 命名和 displayName 屬性
- 如需建立來源地圖,請參閱封閉編譯器來源
- 以下列舉幾個 GWT 來源對應支援的螢幕截圖和支援服務
來源對應是開發人員工具集中強大的工具。將網頁應用程式保持精簡,但可輕鬆進行偵錯,是非常有用的工具。此外,這對剛起步的開發人員來說,也是十分強大的學習工具。透過這項工具,開發人員可以查看有經驗的開發人員建構應用程式,並撰寫自己的應用程式,完全不必徹底處理無法讀取的壓縮程式碼。
別再猶豫了,立即開始為所有專案產生來源對應!