天天看點

前端建構:Source Maps詳解

一、前言                          

 當使用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

  預編譯後得到如下js

   當需要調試時我們的處境就是看着js代碼修改clojurescript代碼,對于這個becomegeek函數來說沒多大困難,但對于整個工程來說難度

不亞于看着二進制中間碼來修改java代碼哦。下面我們通過lein+cljsbuild插件來生成source maps進而解決上述問題!

  project.clj配置資訊

  執行lein指令

 然後我們開啟chrome的devtools中js和css的source maps功能即可像在vs上調試c#一樣爽快了。

前端建構:Source Maps詳解

在sample.cljs檔案中設定斷點,然後調用sample.becomegeek調試即可!

chrome的devtools:

前端建構:Source Maps詳解

ff的devtools:

前端建構:Source Maps詳解

  我想大家現在已經感受到source maps的威力了,有了它我們就可以安心的使用js的超集語言(clojurescript、coffeescript和typescript等),也可安心調試jquery.min.js等經過壓縮混淆的庫代碼了。

    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....... 

  chrome,devtools的settings中開啟js和css的source maps功能。

  

前端建構:Source Maps詳解

  ff,預設已經開啟js和css的source maps功能。

      下面将介紹lessc、gc(google closure compiler)、uglifyjs、clojurescript和coffeescript

      gc,作為js的編譯器,不但提供去除空白、注釋等功能,還會對代碼進行文法分析并優化代碼(函數内聯、變量常量化、局部變量和屬性名替換等

      uglifyjs,由于jquery改用uglifyjs作為其預編譯工具令其聲名遠播,通過下面的指令生成.map檔案:

        以第二節生成的.map檔案為例

         {number} version,source map的版本,目前為3;

         {string} file ,編譯後的檔案路徑;

         {array.<string>} sources ,源碼檔案路徑數組;

         {string} sourceroot ,源碼檔案的所在目錄;

         {array.<string>} names ,源碼中的所有變量名和屬性名;

         {string} mappings ,記錄源碼與編譯後代碼的位置資訊。

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

    ①以分号(;)辨別編譯後代碼的每一行,即是分号間隔的内容代表編譯後代碼的一行;

    ②以逗号(,)辨別編譯後代碼該行中的每一個映射位置,即是逗号間隔的内容代表一個映射位置;

    ③以5組vlq編碼字段辨別源碼和編譯後代碼的具體映射資訊。從左至右每組表示如下:

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

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

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

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

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

              注意:每組vlq編碼字段有0~n個vlq編碼字元組成,如qcaauh。

    vlq編碼最早用于midi檔案,後來被多種格式采用。它的特點就是可以非常精簡地表示很大的數值。

    vlq編碼是變長的。如果(整)數值在-15到+15之間(含兩個端點),用一個字元表示;超出這個範圍,就需要用多個字元表示。并且規定每6bit辨別一個字元。

        第一位(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編碼字元集)

前端建構:Source Maps詳解

  通過chrome和ff下devtools的network面闆我們可以看到浏覽器加載了.map檔案和源代碼檔案,現在問題來了,那麼在生産環境當中使用者通路網頁時豈不會多加載兩個開發環境使用的檔案嗎?

  其實浏覽器預設情況下(不打開devtools時)是不會加載.map檔案和源代碼檔案的,是以大家可以放心了。假如你還是怕使用者誤操作打開了devtools,那麼就在打包釋出時不生成.map檔案就好了!

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