1. 簡(jiǎn)介
sourceMap,顧名思義,就是對(duì)源文件的映射。比如打包壓縮后的代碼對(duì)應(yīng)源文件中的哪一行代碼,這能夠極大地方便開發(fā)者的調(diào)試。
2. sourceMap的作用
我們用一個(gè)簡(jiǎn)單的示例,來看一下sourceMap的作用。如圖,先精簡(jiǎn)一下之前的項(xiàng)目文件。

將sidebar.js中的console,log故意攜程console.lo,修改webpack_config.js如下:
// webpack.config.js
var HtmlWebpackPlugin = require('html-webpack-plugin');
var { CleanWebpackPlugin } = require('clean-webpack-plugin');
var path = require('path');
module.exports = {
entry: {
index: "./src/index.js",
},
devtool: "none",
output: {
path: path.resolve(__dirname, 'dist'),
filename: "[name].js"
},
mode: 'development',
plugins: [new HtmlWebpackPlugin({
template: "./src/index.html"
}), new CleanWebpackPlugin()]
};
打包后打開index.html,我們發(fā)現(xiàn)網(wǎng)頁(yè)上沒有sidebar。打開調(diào)試模式有報(bào)錯(cuò)。

點(diǎn)擊報(bào)錯(cuò)提示,我可以看到光標(biāo)直接在出問題的代碼處閃爍。但問題是,我們并不是直接修改打包后的文件來修復(fù)問題,而是去修改源文件來解決問題。所以,我們希望能夠直接定位到出錯(cuò)的源代碼。
3. source map
source map是用來記錄轉(zhuǎn)換后代碼和源代碼之間映射關(guān)系的代碼,當(dāng)客戶端瀏覽器在收到打包后的js文件時(shí),它根據(jù)指定的sourcmap文件把打包的的js代碼轉(zhuǎn)換成格式規(guī)范的js代碼。
也就是說,瀏覽器運(yùn)行轉(zhuǎn)換后的代碼出錯(cuò)時(shí)定位到源代碼位置的關(guān)鍵因素在于:
有記錄映射關(guān)系的source_map,并能告知瀏覽器如何讀取。
我們配置如下屬性:
devtool: "source-map"
運(yùn)行打包命令后,如下:


生成了一個(gè)單獨(dú)的source map文件用來記錄映射關(guān)系,同時(shí)在打包生成的代碼末尾注明了source map文件的地址。
點(diǎn)擊報(bào)錯(cuò)信息,如下,點(diǎn)擊后直接定位到了出錯(cuò)的源碼。

總結(jié):整個(gè) source map 作為一個(gè)單獨(dú)的文件生成。它為 bundle 添加了一個(gè)引用注釋,以便開發(fā)工具知道在哪里可以找到它??捎糜谏a(chǎn)環(huán)境和開發(fā)環(huán)境。對(duì)于生產(chǎn)環(huán)境,應(yīng)該將服務(wù)器配置為,不允許普通用戶訪問 source map 文件。
優(yōu)點(diǎn):能夠準(zhǔn)確映射到原始源代碼
缺點(diǎn):由于攜帶了最完整的映射信息,所以構(gòu)建和重構(gòu)速度很慢
我們目前使用的是生成一個(gè)完整的map文件的形式來記錄映射信息,那么還有沒有別的方法呢?
3.1 inine 關(guān)鍵字
顧名思義,內(nèi)聯(lián)。就是將.map文件作為DataURI嵌入,不單獨(dú)生成.map文件。他必須與其它關(guān)鍵字配合使用,如下:
devtool: "inline-source-map"
打包后如下:

可以看到?jīng)]有map文件了,底部的 sourceMappingURL 從文件地址變成了base64編碼。等于是將原來單獨(dú)的map文件編碼城base64內(nèi)嵌到了打包輸出的js文件中。
點(diǎn)擊報(bào)錯(cuò)信息后直接定位到了出錯(cuò)的源碼。
總結(jié): source map 轉(zhuǎn)換為 DataUrl 后添加到 bundle 中。僅用于開發(fā)環(huán)境
優(yōu)點(diǎn):能夠準(zhǔn)確映射到原始源代碼,相較source-map,可以少加載一個(gè)文件
缺點(diǎn):由于攜帶了最完整的映射信息,所以構(gòu)建和重構(gòu)速度很慢。相較source-map,打包后的index.js文件變大了。
3.2 hidden 關(guān)鍵字
隱藏源文件。僅有一種搭配:
devtool: "hidden-source-map"
打包后如下:

沒有引用地址注釋了,但仍然可以準(zhǔn)確定位到源文件信息。

總結(jié):與 source-map 相同,但不會(huì)為 bundle 添加引用注釋。如果你只想 source map 映射那些源自錯(cuò)誤報(bào)告的錯(cuò)誤堆棧跟蹤信息,但不想為瀏覽器開發(fā)工具暴露你的 source map,這個(gè)選項(xiàng)會(huì)很有用??捎糜陂_發(fā)環(huán)境和生產(chǎn)環(huán)境。生產(chǎn)環(huán)境中,不應(yīng)將 source map 文件部署到 web 服務(wù)器。而是只將其用于錯(cuò)誤報(bào)告工具。
ps:inline可以和大多數(shù)關(guān)鍵字配合使用,作用是將source map進(jìn)行內(nèi)聯(lián)。
3.3 eval 關(guān)鍵字
直接將每個(gè)模塊使用 eval 執(zhí)行。這里可以單獨(dú)使用,也可以搭配其他關(guān)鍵字使用。
3.3.1 eval
devtool: "eval"


總結(jié):每個(gè)模塊都使用 eval() 執(zhí)行,并且都有 //@ sourceURL。僅用于開發(fā)環(huán)境。
優(yōu)點(diǎn):此選項(xiàng)會(huì)非??斓貥?gòu)建。
缺點(diǎn):由于會(huì)映射到轉(zhuǎn)換后的代碼,而不是映射到原始代碼(沒有從 loader 中獲取 source map),所以不能正確地顯示行數(shù)。
3.3.2 eval-source-map
devtool: "eval-source-map"


總結(jié):每個(gè)模塊使用 eval() 執(zhí)行,并且 source map 轉(zhuǎn)換為 DataUrl 后添加到 eval() 中。僅用于開發(fā)環(huán)境。
優(yōu)點(diǎn):重新構(gòu)建時(shí)有比較快的速度,并且生成實(shí)際的文件。行數(shù)能夠正確映射,因?yàn)闀?huì)映射到原始代碼中。它會(huì)生成用于開發(fā)環(huán)境的最佳品質(zhì)的 source map。
缺點(diǎn):初始化 source map 時(shí)比較慢
3.3.3 搭配其他關(guān)鍵字
eval 還可以搭配其他關(guān)鍵字,下面會(huì)講到。但不管搭配什么,它的作用都是,將每個(gè)模塊都使用 eval() 執(zhí)行。
4. source map詳細(xì)程度
目前為止,我們都是拿到了所有模塊的完整映射信息。但是,十幾條是的時(shí)候我們并不需要這么晚完整的映射信息。那么有什么辦法來減少不必要的映射信息,加快構(gòu)建速度呢?
4.1 cheap 關(guān)鍵字
作用: 顧名思義,低配的。不包含列信息,也不包含loader的sourcemap??纱钆浯蟛糠制渌P(guān)鍵字使用。比如:
devtool: "cheap-source-map"
sourceMap文件默認(rèn)會(huì)保存打包代碼和源代碼之間的行與列的映射信息,文件內(nèi)容較復(fù)雜時(shí),報(bào)錯(cuò)會(huì)攜帶列信息。但如果指明了cheap,就不需要映射列信息,出錯(cuò)時(shí)只會(huì)攜帶行信息。另外,不包含loader的 sourcemap(例如 babel 的 sourcemap)。


如圖,僅定位到了錯(cuò)誤的行。
總結(jié):沒有列映射(column mapping)的 source map,忽略 loader source map。映射的是轉(zhuǎn)換過的代碼(僅限行)??捎糜陂_發(fā)環(huán)境和生產(chǎn)環(huán)境。
優(yōu)點(diǎn):構(gòu)建速度較快。
缺點(diǎn):映射信息品質(zhì)降低。
4.2 module 關(guān)鍵字
module關(guān)鍵字僅在 cheap 關(guān)鍵字存在的情況下使用。用來提升映射信息品質(zhì),輸出模塊映射的行信息。
devtool: "cheap-module-source-map"


總結(jié):沒有列映射(column mapping)的 source map,將 loader source map 簡(jiǎn)化為每行一個(gè)映射(mapping)??捎糜陂_發(fā)環(huán)境和生產(chǎn)環(huán)境。
優(yōu)點(diǎn):提升了映射信息品質(zhì),映射到原始源代碼(僅限行)。
缺點(diǎn):品質(zhì)和速度都是中等,并無(wú)突出的地方。
5. 最佳模式
以上關(guān)鍵字,都擁有自己的行為,搭配起來,就是將不同的行為進(jìn)行組合。
- source-map 生成映射信息文件
- inline 將映射信息內(nèi)聯(lián)
- eval 將模塊用eval包裹執(zhí)行。含有這個(gè)關(guān)鍵字就不能用于生產(chǎn)環(huán)境。
- cheap 降低映射信息品質(zhì),沒有列信息,沒有l(wèi)oader的映射信息
- module 必須與cheap搭配,仍然沒有列信息,但是會(huì)映射loader的行信息
基于以上特征,我們?cè)谏a(chǎn)和開發(fā)中的最佳搭配模式如下:
開發(fā):cheap-module-eval-source-map
生產(chǎn):cheap-module-source-map
補(bǔ)充
原理后續(xù)研究一下。
參考
https://www.webpackjs.com/configuration/devtool/
https://webpack.js.org/configuration/devtool/
Webpack中的sourcemap
MDN - 使用 source map
解決Failed to parse SourceMap: http:xxx 問題
淺談webpack devtool里的7種SourceMap模式