前端構建:Source Maps詳解

1、前言                          
javascript

 當使用CoffeeScript、ClojureScript編寫前端腳本時,當使用Less、Sacc編寫樣式規則時,是否以爲調試時沒法準確找到源碼位置呢?當使用jquery.min.js等經壓縮後的工具庫時,是否以爲連調試的門都不不知道在哪呢?css

  針對上述問題,google爲咱們提供了Source Maps這一解決方案,如下內容爲對Source Maps的學習記錄,以便往後查閱。html

  因爲篇幅較長,特設目錄一坨!前端

  2、示例java

  3、Source Maps方案詳解jquery

       1. 方案結構apache

       2. 支持的瀏覽器和啓動方式json

   3. 生成器api

   4. map文件詳解數組

    4.1. map文件格式

    4.2. mappings屬性

    4.3. VLQ編碼

  4、注意

  5、總結

 

2、示例                          

  首先咱們使用ClojureScript寫一段遞歸函數becomeGeek

(ns sample)
(defn becomeGeek [progress] 
    (.log js/console progress)
    (if (> 100 progress)
        (becomeGeek (+ 1 progress))))

  預編譯後獲得以下js

sample.becomeGeek = (function becomeGeek(process){
    console.log(process);

    if(((100) > process)){
        return becomeGeek.call(null,((1) + process));
    } else {
        return null;
    }
});

  當須要調試時咱們的處境就是看着JS代碼修改ClojureScript代碼,對於這個becomeGeek函數來講沒多大困難,但對於整個工程來講難度不亞於看着二進制中間碼來修改Java代碼哦。下面咱們經過lein+cljsbuild插件來生成source maps從而解決上述問題!

  project.clj配置信息

(defproject sample "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [org.clojure/clojurescript "0.0-2411"
                  :exclusions [org.apache.ant/ant]]
                 [compojure "1.1.6"]]
  :plugins [[lein-cljsbuild "1.0.4"]]
  :cljsbuild {
    :builds [{:id "main"
              :source-paths ["src-cljs"]
              :compiler {:output-to "js/main.js"
                           :output-dir "out"
                         :optimizations :none
                         :source-map true}}]})

  執行lein命令

$ lein cljsbulid once

 而後咱們開啓Chrome的devTools中js和css的source maps功能便可像在VS上調試C#同樣爽快了。

在sample.cljs文件中設置斷點,而後調用sample.becomeGeek調試便可!

Chrome的devTools:

FF的devTools:

3、Source Maps方案詳解                    

  我想你們如今已經感覺到Source Maps的威力了,有了它咱們就能夠安心的使用JS的超集語言(ClojureScript、CoffeeScript和TypeScript等),也可安心調試jquery.min.js等通過壓縮混淆的庫代碼了。

  1. 方案結構

    Source Maps不只僅是一個.map後綴的文件,而是由瀏覽器、.map文件生成器和.map文件組成的一套技術方案。

          .map文件,實際上是一個關係映射文件,用於存放源碼和編譯後代碼的文件、行號、列號和變量名的映射關係;

          .map文件生成器,每種預處理器(Lessc、Closure、cljsc等)均可經過可選項設置如何生成.map文件;

          瀏覽器,Chrome和FF均提供Source Maps支持(IE11依然不支持),瀏覽器實質上提供的是.map文件解析引擎,根據.map文件內容加載源文件和在調試模式中關聯源碼和編譯後代碼。

   另外編譯後代碼最後一行會追加一行指向.map文件語句,指向的方式有http uri scheme 和 data uri scheme兩種。

           http uri scheme,格式爲 //# sourceMappingURL=sample.js.map?rel=1420853090118 

           data uri scheme,就是經過對.map文件進行base64編碼,而後編譯後代碼最後一行以data uri scheme的形式引入.map文件內容,格式爲 //# sourceMappingURL=data:application/json;base64,Asdi....... 

  2. 支持的瀏覽器及啓用方式

  Chrome,devTools的Settings中開啓JS和CSS的Source Maps功能。

  

  FF,默認已經開啓JS和CSS的Source Maps功能。

  3. 生成器

      下面將介紹Lessc、GC(Google Closure Compiler)、UglifyJS、ClojureScript和CoffeeScript

      Less的生成器爲lessc,經過可選項 --source-map 開啓生成.map文件的功能,並經過如 --source-map-rootpath 等可選項配置.map文件的相關信息。具體請查看《前端構建:Less入了個門

      GC,做爲JS的編譯器,不但提供去除空白、註釋等功能,還會對代碼進行語法分析並優化代碼(函數內聯、變量常量化、局部變量和屬性名替換等)

a = new Object    => a = {}
a = new Array     => a = []
if (a) b()        => a && b()
return 2 * 3;     => return 6;
   GC提供三種調用方式,分別爲網頁版網絡API版獨立應用程序。因爲GC使用Java編寫,所以咱們須要安裝JRE。(若不想安裝JRE那麼可參考@趙劼經過IKVM.NET來將clojure-compiler.jar轉碼爲.Net版)而後經過下面的命令生成.map文件:
$ java -jar compiler.jar --js sample.js --create_source_map ./sample-map --js_output_file sample.min.js
 

      UglifyJS,因爲jQuery改用UglifyJS做爲其預編譯工具令其聲名遠播,經過下面的命令生成.map文件:

$ uglifyjs sample.js -o sample.min.js --source-map sample.min.map

ClojureScript,咱們能夠經過第二節的方式生成.map文件。
CoffeeScript,經過下面的命令生成.map文件:
coffee -c sample.min.js sample.js -m
 

  4. map文件詳解

     到這裏你們已經能夠駕輕就熟地使用Source Maps了,接下來的內容是爲想再深刻理解.map文件內容和Source Maps實現原理的朋友準備的。內容主要來自@阮一峯的《Javascript Source Map 詳解》

     4.1. map文件格式

        以第二節生成的.map文件爲例

{"version":3,
 "file":"/C:/lein/myapp/out/sample.js",
 "sources":["sample.cljs?rel=1420853090124"],
 "sourceRoot":"","mappings":
 ";AAAA;;AAEA,oBAAA,pBAAMA,yCAAYC;AAAlB,AACC,AAAMC,YAAWD;;AACjB,GAAI,CAAA,QAAOA;AACV,OAACE,qBAAW,CAAA,MAAKF;;AADlB",
 "names":["sample/becomeGeek", "process", "js/console", "becomeGeek"]}

         {Number} version,Source map的版本,目前爲3;

         {String} file ,編譯後的文件路徑;

         {Array.<String>} sources ,源碼文件路徑數組;

         {String} sourceRoot ,源碼文件的所在目錄;

         {Array.<String>} names ,源碼中的全部變量名和屬性名;

         {String} mappings ,記錄源碼與編譯後代碼的位置信息。

     4.2. mappings屬性

        首先mapping屬性值分爲三層含義

    ①以分號(;)標識編譯後代碼的每一行,便是分號間隔的內容表明編譯後代碼的一行;

    ②以逗號(,)標識編譯後代碼該行中的每個映射位置,便是逗號間隔的內容表明一個映射位置;

    ③以5組VLQ編碼字段標識源碼和編譯後代碼的具體映射信息。從左至右每組表示以下:

              第1組,表示對應編譯後代碼的第幾列;

              第2組,表示源碼所屬文件在sources數組中的索引值;

              第3組,表示對應源碼的第幾行;

              第4組,表示對應源碼的第幾列;

              第5組,表示在names數組中的索引值,若沒有則可省略。

              注意:每組VLQ編碼字段有0~N個VLQ編碼字符組成,如qCAAUH。

     4.3. VLQ編碼               

    VLQ編碼最先用於MIDI文件,後來被多種格式採用。它的特色就是能夠很是精簡地表示很大的數值。

    VLQ編碼是變長的。若是(整)數值在-15到+15之間(含兩個端點),用一個字符表示;超出這個範圍,就須要用多個字符表示。而且規定每6bit標識一個字符。

 Continuation
  |     Sign
  |     |
  V     V
  101011

 

        第一位(Continuation位)表示當前6個bit是否爲當前編碼段的最後一節,1表示不是,0表示是。

        最後一位(Sign位),當該節爲當前編碼段的第一節時,表示符號1爲負號,0爲正號;若不是第一節則表示數值位。

        下面對16進行VLQ編碼,

           1. 將16轉換爲二進制10000;

           2. 在最右邊補充符號位(Sign位)獲得100000;

           3. 從最右邊開始以5bit爲一組對其進行分段,分段後不足5bit的在前面補0,獲得0000一、00000;

           4. 倒序獲得00000、00001;

           5. 爲每一段添加連續位(Continuation位)獲得100000、000001;

           6. 對每段進行Base64編碼,獲得gB。(下圖爲Base64編碼字符集)

                       

 

4、注意                                

  經過Chrome和FF下devTools的network面板咱們能夠看到瀏覽器加載了.map文件和源代碼文件,如今問題來了,那麼在生產環境當中用戶訪問網頁時豈不會多加載兩個開發環境使用的文件嗎?

  其實瀏覽器默認狀況下(不打開devTools時)是不會加載.map文件和源代碼文件的,因此你們能夠放心了。假如你仍是怕用戶誤操做打開了devTools,那麼就在打包發佈時不生成.map文件就行了!

 

5、總結                                     

  以前嘗試過CoffeeScript,但因爲編碼速度雖然提升很多,但調試效率卻下降更多(without source maps之痛),致使最終迴歸JS的懷抱了。如今咱們終於能夠安心使用CoffeeScript咯!

  尊重原創,轉載請註明來自:http://www.cnblogs.com/fsjohnhuang/p/4208566.html ^_^肥仔John

 

6、參考                                    

《Javascript Source Map 詳解》

《Source Maps 介紹》

Source Map Revision 3 Proposal

相關文章
相關標籤/搜索