一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - 編程技術 - Webpack 實戰系列一:正確使用 Sourcemap

Webpack 實戰系列一:正確使用 Sourcemap

2021-12-15 23:08Tecvan范文杰 編程技術

Sourcemap 協議最初由 Google 設計并率先在 Closure Inspector 實現,它能夠將經過壓縮、混淆、合并的代碼還原回未打包狀態,幫助開發者在生產環境中精確定位問題發生的行列位置。

Webpack 實戰系列一:正確使用 Sourcemap

一、什么是 Sourcemap

Sourcemap 協議最初由 Google 設計并率先在 Closure Inspector 實現,它能夠將經過壓縮、混淆、合并的代碼還原回未打包狀態,幫助開發者在生產環境中精確定位問題發生的行列位置。

Webpack 實戰系列一:正確使用 Sourcemap

發展至今,Sourcemap 已廣泛受 Webpack、Rollup、Babel、Less、Typescript、Chrome、Safari、VS Code 等工具支持。

Webpack 實戰系列一:正確使用 Sourcemap

參考:https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k

實現上,Sourcemap 由三部分組成:

  • 開發者編寫的原始代碼
  • 經過 Webpack、Rollup 等工程化工具壓縮、轉化、合并后的產物,且產物中必須包含指向 Sourcemap 文件地址的 //# sourceMappingURL=https://xxxx/bundle.js.map 指令
  • 記錄原始代碼與經過工程化處理代碼之間位置映射關系 Map 文件

頁面初始運行時只會加載編譯構建產物,直到特定事件發生 —— 例如在 Chrome 打開 Devtool 面板時,才會根據 //# sourceMappingURL 內容自動加載 Map 文件,并按 Sourcemap 協議約定的映射規則將代碼重構還原回原始形態,這既能保證終端用戶的性能體驗,又能幫助開發者快速還原現場,提升線上問題的定位與調試效率。

1.1 示例

以 Webpack 為例,設置 devtool = 'source-map' 即可同時打包出代碼產物 xxx.js 文件與同名 xxx.js.map 文件,Map 文件通常為 JSON 格式,內容如:

  1. {
  2. "version": 3,
  3. "sources": [
  4. "webpack:///./src/index.js"
  5. ],
  6. "names": ["name", "console", "log"],
  7. "mappings": ";;;;;AAAA,IAAMA,IAAI,GAAG,QAAb;AAEAC,OAAO,CAACC,GAAR,CAAYF,IAAZ,E",
  8. "file": "main.js",
  9. "sourcesContent": [
  10. "const name = 'tecvan';\n\nconsole.log(name)"
  11. ],
  12. "sourceRoot": ""
  13. }

各字段含義分別為:

  • version:指代 sourcemap 版本,目前最新版本為 3names:字符串數組,記錄原始代碼中出現的變量名
  • file:字符串,該 Sourcemap 文件對應的編譯產物文件名
  • sourcesContent:字符串數組,原始代碼的內容
  • sourceRoot:字符串,源文件根目錄
  • sources:字符串數組,原始文件路徑名,與 sourcesContent 內容一一對應
  • mappings:字符串數組,記錄打包產物與原始代碼的位置映射關系

使用時,瀏覽器會按照 mappings 記錄的數值關系,將產物代碼映射回 sourcesContent 數組所記錄的原始代碼文件、行、列位置,這里面最復雜難懂的點就在于 mappings 字段的規則。

1.2 源碼映射與 VLQ

Sourcemap 最初版本生成的 .map 文件非常大,體積大概為編譯產物的 10 倍;V2 引入 base64 編碼等算法將之減少 20% ~ 30%;而最新版本 V3 又在 V2 基礎上引入 VLQ 等算法,體積進一步壓縮了 50%。這一系列進化造就了一個效率極高的 Sourcemap 體系,但伴隨而來的則是較為復雜的 mappings 編碼規則。

1.2.1 mappings 編碼規則

舉個例子,對于下面的代碼:

Webpack 實戰系列一:正確使用 Sourcemap

當 devtool = 'source-map' 時,Webpack 生成的 mappings 字段為:

  1. ;;;;;AAAA,IAAMA,IAAI,GAAG,QAAb;AAEAC,OAAO,CAACC,GAAR,CAAYF,IAAZ,E

字段內容包含三層結構:

  • 以 ; 分割的「行映射」,每一個 ; 對應編譯產物每一行到源碼的映射,上例經過分割后:
  1. [
  2. // 產物第 1-5 行內容為 Webpack 生成的 runtime,不需要記錄映射關系
  3. '', '', '', '', '',
  4. // 產物第 6 行的映射信息
  5. 'AAAA,IAAMA,IAAI,GAAG,QAAb',
  6. // 產物第 7 行的映射信息
  7. 'AAEAC,OAAO,CAACC,GAAR,CAAYF,IAAZ,E'
  8. ]
  • 以 , 分割的「片段映射」,每一個 , 對應該行中每一個代碼片段到源碼的映射,上例經過分割后:
  1. [
  2. // 產物第 1-5 行內容為 Webpack 生成的 runtime,不需要記錄映射關系
  3. '', '', '', '', '',
  4. // 產物第 6 行的映射信息
  5. [
  6. // 片段 `var` 到 `const` 的映射
  7. 'AAAA',
  8. // 片段 `name` 到 `name` 的映射
  9. 'IAAMA',
  10. // 等等
  11. 'IAAI', 'GAAG', 'QAAb'],
  12. // 產物第 7 行的映射信息
  13. ['AAEAC', 'OAAO', 'CAACC', 'GAAR', 'CAAYF', 'IAAZ', 'E']
  14. ]

第三層邏輯為片段映射到源碼的具體位置,以上例 IAAMA 為例:

  • 第一位 I 該代碼片段在產物中列數
  • 第二位 A 代表源碼文件的索引,即該片段對標到 sources 數組的元素下標
  • 第三位 A 代表片段在源碼文件的行數
  • 第四位 M 代表片段在源碼文件的列數
  • 第五位 A 代表該片段對應的名稱索引,即該片段對標到 names 數組的元素下標

上述第1、2層邏輯比較簡單,唯一需要注意的是片段之間是一種相對偏移關系,例如對于上例第六行映射值:AAAA,IAAMA,IAAI,GAAG,QAAb,每一個片段的第一位 —— 即片段列數為 A,I,I,G,Q,分別代表:

  • A :第 A 列
  • I :第 A + I 列
  • I :第 A + I + I 列
  • G :第 A + I + I + G 列
  • Q :第 A + I + I + G + Q 列

這種相對偏移能減少 Sourcemap 產物的體積,提升整體性能。

而第三層的片段位置映射則用到了一種比較高效數值編碼算法 —— VLQ(Variable-length Quantity)。

1.2.2 VLQ編碼

參考:https://en.wikipedia.org/wiki/Variable-lengsth_quantity

VLQ 本質上是一種將整數數值轉換為 Base64 的編碼算法,它先將任意大的整數轉換為一系列六位字節碼,再按 Base64 規則轉換為一串可見字符。VLQ 使用六位比特存儲一個編碼分組,例如:

Webpack 實戰系列一:正確使用 Sourcemap

數字 7 經過 VLQ 編碼后,結果為 001110,其中:

  • 第一位為連續標志位,標識后續分組是否為同一數字;
  • 第六位表示該數字的正負符號,0為正整數,1為負整數;
  • 中間第 2-5 為實際數值。

這樣一個六位編碼分組,就可以按照 Base64 的映射規則轉換為 ABC 等可見字符,例如上述數字 7 編碼結果 001110,等于十進制的 14,按 Base64 字碼表可映射為字母 O。

Webpack 實戰系列一:正確使用 Sourcemap

但是,分組中只有中間的 4 個字節用于表示數值,因此單個分組只能表達 「-15 ~ 15」 之間的數值范圍,對于超過這個范圍的整數需要組合多個分組共同表達同一數字,組合規則:

  • 第一個分組的最后一位為符號位,其它分組從 2-6 均為數值位
  • 取二進制值最后四位為第一個分組值,之后從后到前,每 5 位為一個劃分為一個分組
  • 除最后一個分組外,其余分組的連續標志位都設置為 1

例如對于十進制 -17,其二進制為 10001 (取 17 的二進制) 共5位,首先從后到前拆分為兩組,后四位 0001 為第一組,連續標志位為 1,符號位為 1,結果為 1,0001,1;剩下的 1 分配到第二個 —— 也是最后一個分組,連續標志位為 0,結果為 0,00001。按 Base64 規則 [100011, 000001] 最終映射為 jA。

  1. 十進制 二進制 VLQ Base64
  2. -17 => 1,0001 => 100011, 000001 => jA

同樣的,對于更大的數字,例如 1200,其二進制為 10010110000,分組為 [10, 01011, 0000],從后到前編碼,第一個分組為 1,0000,0;第二個分組為 1,01011;最后一個分組為 0,00010。按 Base64 映射為 grC。

  1. 十進制 二進制 VLQ Base64
  2. 1200 => 10;01011;0000 => 100000,101011,000010 => grC

Webpack 實戰系列一:正確使用 Sourcemap

1.2.3 解碼 mappings

結合 VLQ 編碼知識,我們再回過來頭來解讀本章開頭的例子,對于代碼:

Webpack 實戰系列一:正確使用 Sourcemap

編譯生成 mappings:

  1. ;;;;;AAAA,IAAMA,IAAI,GAAG,QAAb;AAEAC,OAAO,CAACC,GAAR,CAAYF,IAAZ,E

按行、片段規則分割后,得出如下片段:

  1. [
  2. // 產物第 1-5 行內容為 Webpack 生成的 runtime,不需要記錄映射關系
  3. '', '', '', '', '',
  4. // 產物第 6 行的映射信息
  5. ['AAAA', 'IAAMA', 'IAAI', 'GAAG', 'QAAb'],
  6. // 產物第 7 行的映射信息
  7. ['AAEAC', 'OAAO', 'CAACC', 'GAAR', 'CAAYF', 'IAAZ', 'E']
  8. ]

以第 6 行 ['AAAA', 'IAAMA', 'IAAI', 'GAAG', 'QAAb'] 為例:

  • AAAA 解碼結果為 [000000, 000000, 000000, 000000],即產物第 6 行「第0列」映射到 sources[0] 文件的「第0行」,「第0列」,實際對應 var 到 const 的位置映射
  • IAAMA 解碼結果為 [001000, 000000, 000000, 001100, 000000],即產物第 6 行第4列映射到 sources[0] 文件的「第0行」,「第6列」,實際對應產物 name 到源碼 name 的位置映射

其它片段以此類推。

二、使用 Sourcemap

Webpack 提供了兩種設置 Sourcemap 的方式,一是通過 devtool 配置項設置 Sourcemap 規則短語;二是直接使用 SourceMapDevToolPlugin 或 EvalSourceMapDevToolPlugin 插件深度定制 Sourcemap 的生成邏輯。

下面我們先展開介紹比較晦澀的 devtool 配置項,理解 Webpack 所提供的各種 Sourcemap 功能規則。

2.1 使用devtooldevtool

支持 25 種字符串枚舉值,包括 eval、source-map、eval-source-map 等,分開來看都特別晦澀,但仔細觀察可發現這些值都是由 inline、eval、source-map、nosources、hidden、cheap、module 七種關鍵詞組合而成,這些關鍵詞各自代表一項 Sourcemap 規則。

2.1.1 eval

當 devtool 值包含 eval 時,生成的模塊代碼會被包裹進一段 eval 函數中,且模塊的 Sourcemap 信息通過 //# sourceURL 直接掛載在模塊代碼內。例如:

  1. eval("var foo = 'bar'\n\n\n//# sourceURL=webpack:///./src/index.ts?")

eval 模式編譯速度通常比較快,但產物中直接包含了 Sourcemap 信息,因此只推薦在開發環境中使用。

2.1.2 source-map

當 devtool 包含 source-map 時,Webpack 才會生成 Sourcemap 內容。例如,對于 devtool = 'source-map',產物會額外生成 .map 文件,形如:

  1. {
  2. "version": 3,
  3. "sources": [
  4. "webpack:///./src/index.ts"
  5. ],
  6. "names": [
  7. "console",
  8. "log"
  9. ],
  10. "mappings": "AACAA,QAAQC,IADI",
  11. "file": "bundle.js",
  12. "sourcesContent": [
  13. "const foo = 'bar';\nconsole.log(foo);"
  14. ],
  15. "sourceRoot": ""
  16. }

實際上,除 eval 之外的其它枚舉值都包含該字段。

2.1.3 cheap

當 devtool 包含 cheap 時,生成的 Sourcemap 內容會拋棄「列」維度的信息,這就意味著瀏覽器只能映射到代碼行維度。例如 devtool = 'cheap-source-map' 時,產物:

  1. {
  2. "version": 3,
  3. "file": "bundle.js",
  4. "sources": [
  5. "webpack:///bundle.js"
  6. ],
  7. "sourcesContent": [
  8. "console.log(\"bar\");"
  9. ],
  10. // 帶 cheap 效果:
  11. "mappings": "AAAA",
  12. // 不帶 cheap 效果:
  13. // "mappings": "AACAA,QAAQC,IADI",
  14. "sourceRoot": ""
  15. }

瀏覽器映射效果:

Webpack 實戰系列一:正確使用 Sourcemap

雖然 Sourcemap 提供的映射功能可精確定位到文件、行、列粒度,但有時在「行」級別已經足夠幫助我們達到調試定位的目的,此時可選擇使用 cheap 關鍵字,簡化 Sourcemap 內容,減少 Sourcemap 文件體積。

2.1.4 modulemodule

關鍵字只在 cheap 場景下生效,例如 cheap-module-source-map、eval-cheap-module-source-map。當 devtool 包含 cheap 時,Webpack 根據 module 關鍵字判斷按 loader 聯調處理結果作為 source,還是按處理之前的代碼作為 source。例如:

Webpack 實戰系列一:正確使用 Sourcemap

注意觀察上例 sourcesContent 字段,左邊 devtool 帶 module 關鍵字,因此此處映射的是包含 class Person 的最原始代碼;而右邊生成的 sourcesContent 則是經過 babel-loader 編譯處理的內容。

2.1.5 nosources

當 devtool 包含 nosources 時,生成的 Sourcemap 內容中不包含源碼內容 —— 即 sourcesContent 字段。例如 devtool = 'nosources-source-map' 時,產物:

  1. {
  2. "version": 3,
  3. "sources": [
  4. "webpack:///./src/index.ts"
  5. ],
  6. "names": [
  7. "console",
  8. "log"
  9. ],
  10. "mappings": "AACAA,QAAQC,IADI",
  11. "file": "bundle.js",
  12. "sourceRoot": ""
  13. }

雖然沒有帶上源碼,但 .map 產物中還帶有文件名、 mappings 字段、變量名等信息,依然能夠幫助開發者定位到代碼對應的原始位置,配合 sentry 等工具提供的源碼映射功能,可在異地還原諸如錯誤堆棧之類的信息。

2.1.6 inline

當 devtool 包含 inline 時,Webpack 會將 Sourcemap 內容編碼為 Base64 DataURL,直接追加到產物文件中。例如對于 devtool = 'inline-source-map',產物:

  1. console.log("bar");
  2. //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOlsiY29uc29sZSIsImxvZyJdLCJtYXBwaW5ncyI6IkFBQ0FBLFFBQVFDLElBREkiLCJmaWxlIjoiYnVuZGxlLmpzIiwic291cmNlc0NvbnRlbnQiOlsiY29uc3QgZm9vID0gJ2Jhcic7XG5jb25zb2xlLmxvZyhmb28pOyJdLCJzb3VyY2VSb290IjoiIn0=

inline 模式編譯速度較慢,且產物體積非常大,只適合開發環境使用。

2.1.7 hidden

通常情況下,產物中必須攜帶 //# sourceMappingURL= 指令,瀏覽器才能正確找到 Sourcemap 文件,

當 devtool 包含 hidden 時,編譯產物中不包含 //# sourceMappingURL= 指令。例如:

Webpack 實戰系列一:正確使用 Sourcemap

兩者區別僅在于編譯產物最后一行的 //# sourceMappingURL= 指令,當你需要 Sourcemap 功能,又不希望瀏覽器 Devtool 工具自動加載時,可使用此選項。你也可以通過以下操作手動打開 Sourcemap:

Webpack 實戰系列一:正確使用 Sourcemap

2.1.8 小結

總結一下,Webpack 的 devtool 值都是由以上七種關鍵字的一個或多個組成,雖然提供了 27 種候選項,但邏輯上都是由上述規則疊加而成,例如:

  • cheap-source-map:代表 「不帶列映射」 的 Sourcemap
  • eval-nosources-cheap-source-map:代表 「以」 **eval** 「包裹模塊代碼」 ,且 **.map** 「映射文件中不帶源碼」 ,且 「不帶列映射」 的 Sourcemap

其它選項以此類推。最后再總結一下:

對于開發環境,適合使用:

  • eval:速度極快,但只能看到原始文件結構,看不到打包前的代碼內容
  • cheap-eval-source-map:速度比較快,可以看到打包前的代碼內容,但看不到 loader 處理之前的源碼
  • cheap-module-eval-source-map:速度比較快,可以看到 loader 處理之前的源碼,不過定位不到列級別
  • eval-source-map:初次編譯較慢,但定位精度最高

對于生產環境,則適合使用:

  • source-map:信息最完整,但安全性最低,外部用戶可輕易獲取到壓縮、混淆之前的源碼,慎重使用
  • hidden-source-map:信息較完整,安全性較低,外部用戶獲取到 .map 文件地址時依然可以拿到源碼
  • nosources-source-map:源碼信息確實,但安全性較高,需要配合 Sentry 等工具實現完整的 Sourcemap 映射

2.2 使用插件

上面介紹的 devtool 配置項本質上只是一種方便記憶、使用的規則縮寫短語,Sourcemap 的底層處理邏輯實際由 SourceMapDevToolPlugin 與 EvalSourceMapDevToolPlugin 插件實現。

參考:https://webpack.js.org/plugins/source-map-dev-tool-plugin/

在 devtool 基礎上,插件還提供了更多更細粒度的配置項,用于滿足更復雜的需求場景,包括:

  • 使用 test、include、exclude 配置項設定對那些 bundle 生成 Sourcemap
  • 使用 append、filename、moduleFilenameTemplate、publicPath 配置項設定 Sourcemap 文件的文件名、URL

使用方法與其它插件無異,如:

  1. const webpack = require('webpack');
  2. module.exports = {
  3. // ...
  4. devtool: false,
  5. plugins: [new webpack.SourceMapDevToolPlugin({
  6. exclude: ['vendor.js']
  7. })],
  8. };

插件配置規則較簡單,此處不贅述。

三、總結

至此,有關 Sourcemap 的大部分內容就講解完畢了,讀者們需要了解 Sourcemap 是一種高效位置映射算法,它將產物到源碼之間的位置關系表達為 mappings 分層設計與 VLQ 編碼規則,再通過 Chrome、Safari、VS Code、Sentry 等工具異地還原為接近開發狀態的源碼形式。

在 Webpack 場景下,通常只需要選擇適當的 devtool 短語即可滿足大多數場景需求,特殊情況下也可以直接使用 SourceMapDevToolPlugin 做更深度的定制化。

原文鏈接:https://mp.weixin.qq.com/s/-y35QBSIx2jMvG5dNklcPQ

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 美女沟厕撒尿全过程高清图片 | 国产资源中文字幕 | 包射屋 | 丝瓜草莓香蕉绿巨人幸福宝 | 精品一区二区三区高清免费不卡 | 成人观看免费观看视频 | 操妈小说| 成人福利网站 | 潘金莲西门庆一级淫片aaaaaa | 久久精品热在线观看30 | 日本道三区播放区 | 欧美a在线观看 | 国产精品网站在线观看 | 精品久久免费视频 | 513热点| 国产成人综合网 | 精品国产成a人在线观看 | 国产中文字幕 | 色欧美在线 | 青青色在线观看 | 成人免费视频大全 | 沉香如屑西瓜视频免费观看完整版 | 精彩国产萝视频在线 | 亚洲欧美综合区自拍另类 | 国内精品九一在线播放 | 性德国高清xxxxbbbb | 好涨好大我快受不了了视频网 | 亚洲国产精品第一页 | 国色天香社区在线 | 人与禽交3d动漫羞羞动漫 | 不卡一区二区三区卡 | 国产成人综合手机在线播放 | 91yellow吧字幕网zmff7 | 欧美精选视频 | 欧美成人aletta ocean| 操美女 | 2020年国产精品午夜福利在线观看 | 女人把私密部位张开让男人桶 | 欧美精品99久久久久久人 | a级毛片毛片免费观看永久 a级黄色片免费 | 精品国产爱久久 |