sourcemap是爲了解決開發代碼與實際運行代碼不一致時幫助咱們debug到原始開發代碼的技術。尤爲是現在前端開發中大部分的代碼都通過編譯,打包等工程化轉換。好比開發環境下用scss寫樣式, 想在瀏覽器中在線編輯css那樣編輯scss就不是那麼容易了。從我本身看過的資料中, sourcemap的概念最先出如今12年, jquer1.9是較早支持sourcemap的庫。這篇博客比較有表明性:Introduction to JavaScript Source Maps,阮一峯的文章JavaScript Source Map 詳解也大量參考該博客。關於sourcemap的原理及做用,基本在這兩篇文章中講清楚了。回到webpack中的sourcemap,就我這幾天的琢磨, 這方面資料相對比較零散,但凡搜索Webpack中sourcemap的配置, 老是能獲得千篇一概的以下信息:
Sourcemap type Quality Notesjavascript
eval: 生成代碼 每一個模塊都被eval執行,而且存在@sourceURLcss
cheap-eval-source-map: 轉換代碼(行內) 每一個模塊被eval執行,而且sourcemap做爲eval的一個dataurlhtml
cheap-module-eval-source-map: 原始代碼(只有行內) 一樣道理,可是更高的質量和更低的性能前端
eval-source-map: 原始代碼 一樣道理,可是最高的質量和最低的性能html5
cheap-source-map: 轉換代碼(行內) 生成的sourcemap沒有列映射,從loaders生成的sourcemap沒有被使用java
cheap-module-source-map: 原始代碼(只有行內) 與上面同樣除了每行特色的從loader中進行映射webpack
source-map: 原始代碼 最好的sourcemap質量有完整的結果,可是會很慢git
webpack中devtool的配置的官方文檔在這 :webpack-devtoolgithub
反正我看完這些說明是雲裏霧裏, 就我本身而言, 有3個疑問:web
eval和sourcemap有什麼關係,eval模式是sourcemap嗎?
包含cheap關鍵字的配置中只有行內是什麼意思?
這幾種不一樣的配置有什麼區別?
看似配置項不少, 其實只是五個關鍵字eval
,source-map
,cheap
,module
,inline
的任意組合。這五個關鍵字每一項都表明一個特性, 這四種特性能夠任意組合。它們分別表明如下五種特性(單獨看特性說明有點不明因此,別急,往下看):
eval: 使用eval包裹模塊代碼
source-map: 產生.map
文件
cheap: 不包含列信息(關於列信息的解釋下面會有詳細介紹)也不包含loader的sourcemap
module: 包含loader的sourcemap(好比jsx to js ,babel的sourcemap)
inline: 將.map
做爲DataURI嵌入,不單獨生成.map
文件(這個配置項比較少見)
瞭解了以上各類不一樣特性, 再來逐一解答以上問題。
eval
和source-map
都是webpack中devtool的配置選項, eval
模式是使用eval
將webpack中每一個模塊包裹,而後在模塊末尾添加模塊來源//# souceURL
, 依靠souceURL
找到原始代碼的位置。包含eval關鍵字的配置項並不單獨產生.map
文件(eval模式有點特殊, 它和其餘模式不同的地方是它依靠sourceURL來定位原始代碼, 而其餘全部選項都使用.map
文件的方式來定位)。包含source-map
關鍵字的配置項都會產生一個.map
文件,該文件保存有原始代碼與運行代碼的映射關係, 瀏覽器能夠經過它找到原始代碼的位置。(注:包含inline
關鍵字的配置項也會產生.map
文件,可是這個map文件是通過base64編碼做爲DataURI嵌入),舉個栗子:eval-source-map
是eval
和source-map
的組合,可知使用eavl
語句包括模塊,也產生了.map
文件。webpack將.map
文件做爲DataURI替換eval
模式中末尾的//# souceURL
。按照我本身的理解, eval
和.map
文件都是sourcemap實現的不一樣方式,雖然大部分sourcemap的實現是經過產生.map
文件, 但並不表示只能經過.map
文件實現。下面是eval模式後產生的模塊代碼:
這裏的列信息指的是代碼的不包含原始代碼的列信息。 官方文檔對於包含cheap
的解釋是這樣的:
> cheap-source-map - A SourceMap without **column-mappings**. SourceMaps > from loaders are not used.
這句話翻譯過來就是「在cheap-source-map模式下sourcemap不包含列信息,也不包含loaders的sourcemap」這裏的「column-mappings」就是代碼列數的意思,是否包含loaders的sourcemap有什麼區別將在以後提到。debug的時候大部分人都只在乎代碼的行數, 不多關注列數, 列數就是該行代碼從第一個字符開始到定位字符的位置(包括空白字符)包含cheap
關鍵字的模式不包含列信息,體如今webpack中就是:若是包含cheap
關鍵字,則產生的.map
文件不包含列信息。也就是說當你在瀏覽器中點擊該代碼的位置時, 光標只定位到行數,不定位到具體字符位置。而不包含cheap
關鍵字時, 點擊控制檯log將會定位到字符位置。
包含列信息後點擊原始代碼的定位,注意光標位置:
不包含列信息的光標位置:
這篇博客:Go to a line number at a specific column直觀地展現了列數的概念。若是深刻到webpack中的細節中體會該配置項,能夠看這篇博客:SurviveJS:Source Maps ,該文章對比了webpack中全部配置項中.map
文件的代碼,這裏截取eval-source-map
和cheap-source-map
的模式產生的.map
文件代碼中的mappings
字段對比:
devtool: 'eval-source-map'
"mappings": "AAAAA,QAAQC,GAAR,CAAY,aAAZ",
devtool: 'cheap-source-map'
"mappings": "AAAA",
注:這裏使用了VLQ編碼,(關於VLQ編碼還可參考這裏:前端構建:Source Maps詳解) 在VLQ編碼中,逗號,
表示字符列分割,分號;
表示行分割。包含cheap
關鍵字的配置項不包含列信息,也就沒有逗號。關於VLQ編碼, 本文最初的阮一峯的文章中有所解釋。而不包含loader的sourcemap指的是不包含loader的sourcemap,不包含它時候若是你使用了諸如babel等代碼編譯工具時, 定位到的原始代碼將是通過編譯後的代碼位置,而非原始代碼。
好比當我用babel編譯JS的時候,若是包含不包含loaders的sourcemap,此時debug到的將是編譯後的代碼, 而非原始代碼,如圖(這是使用cheap-source-map模式未包含loaders的sourcemap狀況下的截圖, debug的位置與以前的對比截圖是同一個地方):
經過以上兩個問題的解釋, webpack中的sourcemap各個配置項異同應該有了必定認識,乍看之下各個配置項很難記憶, 但其實從每一個關鍵字所表明的特性入手, 就能體會到他們的異同。他們在webpack中的主要區別一個體如今重構的性能上, 總的來講eval
性能最好,source-map
性能最低,但就我自身的實踐來看大多用的是最完整的source-map
,該模式對於不論是js仍是css,scss等都能很好的覆蓋, 相反其餘模式都不完整, 在開發環境下重構性能彷佛比不上功能的完善。
另外須要補充的是module
關鍵字, 當加上module
關鍵字webpack將會添加loader的sourcemap。