Webfunny知識分享:webpack sourceMap解析原始碼

Webfunny前端監控發表於2020-08-25

前端的業務越來越龐大,導致我們需要引入的js等靜態資原始檔的體積也越來越大,不得不使用壓縮js檔案的方式來提高載入的效率。

編譯工具的誕生,極大地方便了我們處理js檔案的這一過程,但壓縮後的js檔案極難閱讀,也難以除錯,所以就產生了sourcemap這個功能。

webpack開啟sourcemap功能可以通過壓縮程式碼的堆疊行、列號定位到原始碼的具體位置,我們就以webpack為例來看看如何利用sourcemap反向定位線上原始碼。

 

SourceMap是一種對映關係。當專案執行後,如果出現錯誤,我們可以利用sourceMap反向定位到原始碼。在chrome瀏覽器裡邊解析當然是非常強大,也非常方便了,但是我們想對線上的壓縮程式碼進行逆向定位,像這樣遠端解析就有些難度了。解析工具就是npm:source-map;

正常的sourceMap配置如下:

const path = require('path');

module.exports = {
  devtool: 'source-map', // SourceMap的模式(見下表)
  entry: './src/index.js',  // 入口檔案
  output: {
    filename: 'bundle.js',  // 檔名
    path: path.resolve(__dirname, 'dist')  // 資料夾
  }
}

SourceMap不同模式的特點(見下表)

模式解釋
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 也被簡化為只包含對應行的。

webpack版本不同,生成source-map的方式也不同,今天我們就以webpack2.0+ 、和webpack4.0+這兩個版本來講講sourcemap的配置和解析,其他版本沒試過,原理相同,心累?。

一、webpack2.0+配置devtool,生成sourceMap

webpack2.0+關於sourceMap的正確配置如下:

const buildConfig = {
    mode: "production",
    output: {
        path: distPath,
        filename: "./js/[name].[hash].min.js",
        publicPath: "./"
    },
    plugins: [
        new UglifyJSPlugin({ // 1. 這個配置必須
            sourceMap: true
        }),
    ].concat(baseConfig.htmlArray),
    devtool: "source-map" // 2. 這個配置必須
}

從webpack的文件上可以看到,只要設定devtool選項就可以了,但是你是無法解析出來,為什麼?因為你生成的sourceMap檔案中,沒有包含sourcesContent這個屬性,所以無法解析出原始碼的內容。

如果你加上 UglifyJSPlugin這個外掛以後,並且配置了sourceMap屬性後,就能夠正常生成帶sourcesContent屬性的.map檔案了。

 

所以在你想要利用sourceMap反向定位原始碼的時候,就需要保證你的.map檔案包含sourcesContent這個屬性就可以了,我們來看看解析的效果:

就這樣,webpack2.0+版本的sourceMap就可以這樣解析出來了。 

 

二、webpack4.0+配置devtool,生成sourceMap

webpack4.0生成souceMap的方式非常簡單,先去看webpack的官網,文件非常詳細、種類繁多。但是想要通過這些方式對線上壓縮過的js程式碼進行逆向解析,那簡直是不可能。當然,webpack的這個配置也不是為了讓你去解析線上壓縮程式碼的,在瀏覽器的devtool裡解析解析就好了。廢話不多說,進入正題。

第一步、生成生產環境min.js 和 min.js.map檔案

生產版的檔案要壓縮體積,所以必須配置optimization.minimize=true,但是同時也讓mim.js.map檔案失去了sourcesContent屬性,因此無法解析出原始碼了。配置如下:

const buildConfig = {
    mode: "production",
    output: {
        path: distPath,
        filename: "./js/[name].[hash].min.js",
        publicPath: "./"
    },
    optimization: {    // 1. 這個配置必須
        minimize: true
    },
    plugins: [
    ].concat(baseConfig.htmlArray),
    devtool: "source-map" // 2. 這個配置必須
}

第二步、生成本地環境min.js.map檔案

生成本版min.js.map檔案,配置optimization.minimize=true,告訴webpack不壓縮js程式碼,這樣生成的.min.js.map檔案就能夠包含最全面的原始碼,從而能夠反向定位原始碼了。配置如下:

const buildConfig = {
    mode: "production",
    output: {
        path: distPath,
        filename: "./js/[name].[hash].min.js",
        publicPath: "./"
    },
    optimization: {    // 1. 這個配置必須
        minimize: false
    },
    plugins: [
    ].concat(baseConfig.htmlArray),
    devtool: "source-map" // 2. 這個配置必須
}

第三步、解析生產環境min.js.map檔案,獲得本地環境min.js.map檔案的行列號

通過第一次解析生產版的sourceMap檔案,可以得到本地版sourceMap檔案中原始碼的位置,我們得到了新的行列號:A、B。為我們下一次解析做準備。

第四步、根據第三步獲得的新行列號A、B,解析真正的原始碼

將新的行列號A,B代入到本地版的sourceMap檔案中,就可以解析出真正的原始碼位置了,結果如下圖:

因為webpack4.0以上生成和解析sourceMap的步驟相對較為複雜,所以很少能夠在網上找到真正能夠解析成功的文件。

到此,如何利用sourceMap反向定位生產環境的原始碼位置,就講解完了,你學會了嗎。

 

相關文章