文章出處

一、前言                          

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

  針對上述問題,google為我們提供了Source Maps這一解決方案,以下內容為對Source Maps的學習記錄,以便日后查閱。

  由于篇幅較長,特設目錄一坨!

  二、示例

  三、Source Maps方案詳解

       1. 方案結構

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

   3. 生成器

   4. map文件詳解

    4.1. map文件格式

    4.2. mappings屬性

    4.3. VLQ編碼

  四、注意

  五、總結

 

二、示例                          

  首先我們使用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:

三、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,得到00001、00000;

           4. 倒序得到00000、00001;

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

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

                       

 

四、注意                                

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

  其實瀏覽器默認情況下(不打開devTools時)是不會加載.map文件和源代碼文件的,所以大家可以放心了。假如你還是怕用戶誤操作打開了devTools,那么就在打包發布時不生成.map文件就好了!

 

五、總結                                     

  之前嘗試過CoffeeScript,但由于編碼速度雖然提高不少,但調試效率卻降低更多(without source maps之痛),導致最終回歸JS的懷抱了。現在我們終于可以安心使用CoffeeScript咯!

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

 

六、參考                                    

《Javascript Source Map 詳解》

《Source Maps 介紹》

Source Map Revision 3 Proposal


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()