[webpack] devtool裡的7種SourceMap模式是什麼鬼?

滴滴出行·DDFE發表於2016-11-14

作者:滴滴公共前端團隊 - 水乙

我們在使用 webpack 打包我們的工程模組時,經常會需要 devtool 開啟 sourceMap 讓我們可以除錯程式碼,但是 webpack 文件中關於 devtool 給出了7種模式,文件也寫得非常簡略,初學者很難上手。本文將這7種模式的區別作詳細介紹,希望能對你使用有幫助。

概念有何不同

我們先來看看文件對這7種模式的解釋:

模式 解釋
eval 每個 module 會封裝到 eval 裡包裹起來執行,並且會在末尾追加註釋 //@ sourceURL.
source-map 生成一個 SourceMap 檔案.
hidden-source-map 和 source-map 一樣,但不會在 bundle 末尾追加註釋.
inline-source-map 生成一個 DataUrl 形式的 SourceMap 檔案.
eval-source-map 每個 module 會通過 eval() 來執行,並且生成一個 DataUrl 形式的 SourceMap .
cheap-source-map 生成一個沒有列資訊(column-mappings)的 SourceMaps 檔案,不包含 loader 的 sourcemap(譬如 babel 的 sourcemap)
cheap-module-source-map 生成一個沒有列資訊(column-mappings)的 SourceMaps 檔案,同時 loader 的 sourcemap 也被簡化為只包含對應行的。

注1:webpack 不僅支援這 7 種,而且它們還是可以任意組合上面的 eval、inline、hidden 關鍵字,就如文件所說,你可以設定 souremap 選項為 cheap-module-inline-source-map。

注2:如果你的 modules 裡面已經包含了 SourceMaps ,你需要用 source-map-loader 來和合並生成一個新的 SourceMaps 。

使用結果有何不同

下面我們將列出這7種模式打包編譯後的結果,從中看看他們的異同。

eval

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?"
    )
  },
...])複製程式碼

這樣看很直觀了,正如上文表格中的概念中寫到,eval 模式會把每個 module 封裝到 eval 裡包裹起來執行,並且會在末尾追加註釋。

source-map

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

與此同時,你會發現你的 output 目錄下多了一個 index.js.map 檔案。

我們可以把這個 index.js.map 格式化一下,方便我們在下文的觀察比較:

{
  "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":""
}複製程式碼

關於 sourceMap 行列資訊如何對映原始碼的詳解,此處不是我們要重點討論的話題,從略。感興趣的同學可以參考阮一峰老師的科普文:JavaScript Source Map 詳解

hidden-source-map

webpackJsonp([1],[
  function(e,t,i){...},
  function(e,t,i){...},
  function(e,t,i){...},
  function(e,t,i){...},
  ...
])複製程式碼

與 source-map 相比少了末尾的註釋,但 output 目錄下的 index.js.map 沒有少

inline-source-map

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...複製程式碼

可以看到末尾的註釋 sourceMap 作為 DataURL 的形式被內嵌進了 bundle 中,由於 sourceMap 的所有資訊都被加到了 bundle 中,整個 bundle 檔案變得碩大無比。

eval-source-map

webpackJsonp([1],[
  function(module,exports,__webpack_require__){
    eval(
      ...
      //# sourceMappingURL=data:application/json;charset=utf-8;base64,...
    )
  },
  function(module,exports,__webpack_require__){
    eval(
      ...
      //# sourceMappingURL=data:application/json;charset=utf-8;base64,...
    )
  },  
  function(module,exports,__webpack_require__){
    eval(
      ...
      //# sourceMappingURL=data:application/json;charset=utf-8;base64,...
    )
  },
  ...
]);複製程式碼

和 eval 類似,但是把註釋裡的 sourceMap 都轉為了 DataURL 。

cheap-source-map

和 source-map 生成結果差不多。output 目錄下的 index.js 內容一樣。

但是cheap-source-map生成的 index.js.map 的內容卻比 source-map 生成的 index.js.map 要少很多程式碼,我們對比一下上文 source-map 生成的index.js.map 的結果,發現 source 屬性裡面少了列資訊,只剩一個"webpack:///js/index.js"

// index.js.map
{
  "version":3,
  "file":"js/index.js",
  "sources":["webpack:///js/index.js"],
  "sourcesContent":[...],
  "mappings":"AAAA",
  "sourceRoot":""
}複製程式碼

cheap-module-source-map

// index.js.map
{
  "version":3,
  "file":"js/index.js",
  "sources":["webpack:///js/index.js"],
  "mappings":"AAAA",
  "sourceRoot":""
}複製程式碼

在 cheap-module-source-map 下 sourceMap 的內容更少了,sourceMap 的列資訊減少了,可以看到 sourcesContent 也沒有了。

這麼多模式用哪個好?

開發環境推薦:

cheap-module-eval-source-map

生產環境推薦:

cheap-module-source-map (這也是下版本 webpack 使用-d命令啟動 debug 模式時的預設選項)

原因如下:

  1. 使用 cheap 模式可以大幅提高 souremap 生成的效率。大部分情況我們除錯並不關心列資訊,而且就算 sourcemap 沒有列,有些瀏覽器引擎(例如 v8) 也會給出列資訊。
  2. 使用 eval 方式可大幅提高持續構建效率。參考官方文件提供的速度對比表格可以看到 eval 模式的編譯速度很快。
  3. 使用 module 可支援 babel 這種預編譯工具(在 webpack 裡做為 loader 使用)。
  4. 使用 eval-source-map 模式可以減少網路請求。這種模式開啟 DataUrl 本身包含完整 sourcemap 資訊,並不需要像 sourceURL 那樣,瀏覽器需要傳送一個完整請求去獲取 sourcemap 檔案,這會略微提高點效率。而生產環境中則不宜用 eval,這樣會讓檔案變得極大。

SourceMap 模式效率對比圖

[webpack] devtool裡的7種SourceMap模式是什麼鬼?

更多參考:

webpack.github.io/docs/config…

segmentfault.com/a/119000000…

www.cnblogs.com/hhhyaaon/p/…


歡迎關注DDFE
GITHUB:github.com/DDFE
微信公眾號:微信搜尋公眾號“DDFE”或掃描下面的二維碼

[webpack] devtool裡的7種SourceMap模式是什麼鬼?

相關文章