Webpack中的sourcemap以及如何在生產和開發環境中合理的設定sourcemap的型別

yuxiaoliang發表於2018-07-02

原文在我的部落格中,地址為:原文地址

簡要介紹:在webpack的官網,給出了十幾種sourcemap,那麼每一種sourcemap之間有什麼區別,本文在理解sourcemap的基礎上,分析在生產和開發環境中,應該採用何種形式的sourcemap

一 、 從Sourcemap和Data URL說起

(1)什麼是Sourcemap?

我們在打包中,將開發環境中原始碼經過壓縮,去空格,babel編譯轉化,最終可以得到適用於生產環境的專案程式碼,這樣處理後的專案程式碼和原始碼之間差異性很大,會造成無法debug的問題。

舉例來說,如果壓縮等處理過的生產環境中的程式碼出現bug,除錯的時候只能定位到壓縮處理後的程式碼的位置,無法定位到開發環境中的原始碼。

sourcemap就是為了解決上述程式碼定位的問題,簡單理解,就是構建了處理前的程式碼和處理後的程式碼之間的橋樑。主要是方便開發人員的錯誤定位。這裡的處理操作包括:

I)壓縮,減小體積

II)將多個檔案合併成同一個檔案

III)其他語言編譯成javascript,比如TypeScript和CoffeeScript等

(2)什麼是DataURL?

DataURL最早是出現在HTML檔案img標籤中的關於圖片的引用,DataURL提供了一種將圖片"嵌入"到HTML中的方法。

跟傳統的img的src屬性指向伺服器中某張圖片的地址不同,在Data URL協議中,圖片被轉換成base64編碼的字串形式,並儲存在URL中,冠以mime-type。具體通過DataURL引入圖片例子如下:

<img src="data:image/gif;base64,R0lGODlhMwAxAIAAAAAAAP///
yH5BAAAAAAALAAAAAAzADEAAAK8jI+pBr0PowytzotTtbm/DTqQ6C3hGX
ElcraA9jIr66ozVpM3nseUvYP1UEHF0FUUHkNJxhLZfEJNvol06tzwrgd
LbXsFZYmSMPnHLB+zNJFbq15+SOf50+6rG7lKOjwV1ibGdhHYRVYVJ9Wn
k2HWtLdIWMSH9lfyODZoZTb4xdnpxQSEF9oyOWIqp6gaI9pI1Qo7BijbF
ZkoaAtEeiiLeKn72xM7vMZofJy8zJys2UxsCT3kO229LH1tXAAAOw==">
複製程式碼

DataURL使用於如下的場景

I)訪問外部資源受限

II)圖片體積小,佔用一個HTTP會話資源浪費

二 、 Webpack中的Sourcemap

webpack在打包中同樣支援Sourcemap,並且提供了十幾種的組合。我們以官網給出的為例:

I)eval : 每一個模組都執行eval()過程,並且會追加//@ sourceURL

II)eval-source-map:每一個模組在執行eval()過程之後,並且會為每一個模組生成sourcemap檔案,生成的sourcemap檔案通過DataURL的方式新增

III)cheap-eval-source-map:跟eval-source-map相同,唯一不同的就是增加了"cheap","cheap"是指忽略了行資訊。這個屬性同時也不會生成不同loader模組之間的sourcemap。

VI)cheap-module-eval-source-map:與cheap-eval-source-map相同,但是包含了不同loader模組之間的sourcemap

官網給出的例子容易讓人看懵,因為官網的devtool型別都是以組合形式給出的,實際上webpack中的sourcemap的基本型別包括:eval,cheap,moudule,inline,source-map。其他的型別都是根據這5個基本型別組合而來。我們來具體分析一下這5個基本型別

(1)eval

eval會將每一個module模組,執行eval,執行後不會生成sourcemap檔案,僅僅是在每一個模組後,增加sourceURL來關聯模組處理前後的對應關係。舉例來說:

webpackJsonp([1],[  
function(module,exports,__webpack_require__){    
eval(
      ...      
//# sourceURL=webpack:///./src/js/index.js?'
    )
  },  
function(module,exports,__webpack_require__){    
eval(
      ...      
//# sourceURL=webpack:///./src/static/css/app.less?./~/.npminstall/css-loader/0.23.1/css-loader!./~/.npminstall/postcss-loader/1.1.1/postcss-loader!./~/.npminstall/less-loader/2.2.3/less-loader'
    )
  },  
function(module,exports,__webpack_require__){   
 eval(
      ...      
 //# sourceURL=webpack:///./src/tmpl/appTemplate.tpl?"
    )
  },
...])
複製程式碼

上述是一個指定devtool:eval的壓縮後的程式碼,我們發現壓縮後的程式碼的每一個模組後面都增加了一端包含sourceURL的註釋,sourceURL的值是壓縮前的程式碼,這樣就通過sourceURL關聯了壓縮前後的程式碼,並沒有為每一個模組生成相應的sourcemap。

因為不需要生成模組的sourcemap,因此打包的速度很快。

(2)soure-map source-map會為每一個打包後的模組生成獨立的soucemap檔案,舉例來說:

webpackJsonp([1],[  
function(e,t,i){...},  
function(e,t,i){...},  
function(e,t,i){...},  
function(e,t,i){...},
  ...
])//# sourceMappingURL=index.js.map
複製程式碼

打包後的模組在模組後面會對應引用一個.map檔案,同時在打包好的目錄下會針對每一個模組生成相應的.map檔案,在上例中會生成一個index.js.map檔案,這個檔案是一個典型的sourcemap檔案,形式如下:

{  
"version":3,  
"sources":[
    "webpack:///js/index.js","webpack:///./src/js/index.js",    
    "webpack:///./~/.npminstall/css-loader/0.23.1/css-loader/lib/css-base.js",
    ...
],  
"names":["webpackJsonp","module","exports"...], 
"mappings":"AAAAA,cAAc,IAER,SAASC...",  
"file":"js/index.js",  
"sourcesContent":[...],  
"sourceRoot":""
}
複製程式碼

(3)inline

與source-map不同,增加inline屬性後,不會生成獨立的.map檔案,而是將.map檔案以dataURL的形式插入。如下所示:

webpackJsonp([1],[  
function(e,t,i){...},  
function(e,t,i){...},  
function(e,t,i){...},  
function(e,t,i){...},
  ...
])
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9...
複製程式碼

打包好模組後,在sourceMappingURL中直接將.map檔案中的內容以DataURL的方式引入。

(4)cheap cheap屬性在打包後同樣會為每一個模組生成.map檔案,但是與source-map的區別在於cheap生成的.map檔案會忽略原始程式碼中的列資訊。

devtool: 'eval-source-map'

"mappings": "AAAAA,QAAQC,GAAR,CAAY,aAAZ",
devtool: 'cheap-source-map'

"mappings": "AAAA",
複製程式碼

對比增加cheap和沒有cheap情況下,打包後輸出的.map檔案,在檔案中 使用了VLQ編碼,有"逗號"表示包含了列資訊,顯然增加cheap屬性後,.map檔案中不包含列資訊。

此外增加cheap後也不會有loader模組之間對應的sourcemap,什麼是模組之間的sourcemap呢?

因為webpack最終會將所有的非js資源,通過loader的形式轉變成js資源。比如jsx語言的操作分為:

jsx——(loader)——js——(壓縮等處理)——壓縮後的js

如果沒有loader之間的sourcemap,那麼在debug的時候定義到上圖中的壓縮前的js處,而不能追蹤到jsx中。

(5)module:包含了loader模組之間的sourcemap 這樣差不多就理清了webpack中所有的sourcemap型別。

三 、 在不同的環境中如何選擇sourcemap的型別

在瞭解了webpack中所有的sourcemap基本型別後,我們來分析,如何針對開發環境和生產環境,選擇合理的sourcemap屬性。

(1)首先在原始碼的列資訊是沒有意義的,只要有行資訊就能完整的建立打包前後程式碼之間的依賴關係。因此不管是開發環境還是生產環境,我們都會選擇增加cheap基本型別來忽略模組打包前後的列資訊關聯。

(2)其次,不管在生產環境還是開發環境,我們都需要定位debug到最最原始的資源,比如定位錯誤到jsx,coffeeScript的原始程式碼處,而不是編譯成js的程式碼處,因此,不能忽略module屬性

(3)再次我們希望通過生成.map檔案的形式,因此要增加source-map屬性

總結:

在開發環境中我們使用:cheap-module-eval-source-map

在生產環境中我們使用:cheap-module-source-map。

這裡需要補充說明的是,eval-source-map組合使用是指將.map以DataURL的形式引入到打包好的模組中,類似於inline屬性的效果,我們在生產中,使用eval-source-map會使打包後的檔案太大,因此在生產環境中不會使用eval-source-map。但是因為eval的rebuild速度快,因此我們可以在本地環境中增加eval屬性。

相關文章