你知道source map如何幫你定位源碼么?

大家好,我是若川。今天分享一篇我們經常會忽略的定位原始代碼位置原理的文章。文章不長,例子不錯,可以先收藏,有空時動手試試。

學習源碼系列、年度總結、JS基礎系列


前言

我們知道,代碼上線前要經過壓縮,美化,混淆等步驟,真正上線之后的代碼親媽都不認識。這也可以理解,為了防止別人看到你的源碼發現你的漏洞從而去攻擊你的網頁。

但問題是,如果自己的代碼在線上跑出了bug,連自己都看不懂錯在了哪里。這時候就需要代碼還原工具來幫助我們還原一下代碼,從而找到出錯位置。

這個還原神器就是我們今天的主角source map。今天我們來聊聊它是怎么還原我們的代碼的。

source map在哪

通常,我們用webpack的構建去生成代碼的時候,可以去配置devtool 讓它生成source map,這樣在最后生成的dist就會找到.js.map的文件。

以這個list.js為例

const a = 111;console.log(a);

生成的dist文件

有一行代碼去引用了js.map文件,我們在打開這個文件,可以看到,生成的map文件長這樣

{"version":3,"file":"vote/list/list.c1e192cf.js","sources":["webpack:///webpack/bootstrap","webpack:///./src/pages/vote/list/list.js"],"sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/mpres/zh_CN/htmledition/pages/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 1);\n","var a = 111;\nconsole.log(a);"],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;AClFA;AACA;;;;A","sourceRoot":""}

別看這段那么多,其實也就這幾個字段:

{"version": 3, // source map的版本"file": "",   // 轉換后的文件名"source": [], // 來源文件的代碼"names": [],  // 轉換前所有的變量和屬性名"mapping": "" // 記錄位置信息的字符串
}

這其中真正用于定位的就是這個mapping字段里的信息。

source map 是如何還原代碼的

由于上面的例子過于復雜,這里我們用個簡單的例子來說明一下。

源代碼

/* 注釋 */
var name = "abc";

壓縮后的代碼

var name="abc";
//# sourceMappingURL=a.js.map

對應的source-map

{"version":3,"sources":["a.js"],"names":["name"],"mappings":";AACA,IAAIA,KAAO","file":"a.js","sourcesContent":["/* 注釋 */\nvar name = \"abc\";"]
}

接下來我們看看這個;AACA,IAAIA,KAAO在說什么。

其中分號;代表一個空行。逗號,代表一個位置。

AACA標明的是var的位置,它是先經過VLQ編碼,在經過base64編碼而成。VLQ跟base64不懂都沒什么關系,我們這里知道它是一種編碼方式即可。

下面舉個栗子,來看看188經過VLQ 與 base64編碼的過程及結果。

首先188的二進制表示是10111100,不能滿足VLQ 6字節的要求,所以這里將它拆成兩部分,在交換一下。

接著1100前面補1,因為后面還有一塊block。結尾補0,因為188是一個正數。第一段最終轉出來就是111000。

在看后面一段1011,我們只需要在前面補兩個0。其中第一個0表示沒有block在后面了,第二個0是因為不足5位左邊補0。第二段最終轉出來就是001011。

111000對應VLQ就是56,然后在對應base64的4。

而001011對應VLQ是11,在對應base64就是L。

AACA轉回來就是逆方向操作。

有點麻煩,我估計你們也不想算,所以我們先用一個庫vlq來幫忙轉換一下它。

打印結果如下:

可以看到AACA解析出來是0010。其中,

第一位,表示這個位置在(轉換后的代碼的)的第幾列。

第二位,表示這個位置屬于sources屬性中的哪一個文件。

第三位,表示這個位置屬于轉換前代碼的第幾行。

第四位,表示這個位置屬于轉換前代碼的第幾列。

這里有兩點需要注意:

1、位置都是以0為基數算起。

2、計算的是相對與前一個位置的相對位置。

所以這個0010,這里就代表著var在壓縮后代碼的第0列,對應第0個源碼文件的1行0列。

同理,第二個IAAIA轉過來是4004,這個是相對于上一個字符的位置,所以我們需要加起來,也就是4014。說明name在壓縮后代碼的第4列,對應第0個源碼文件的的第1行,第4列。

第三個KAAO轉過來是5007,相加前面的也就是90111,說明abc是轉換后的代碼第9列,對應第0個源碼文件的的第1行,第11列。

再來個栗子

這是轉換前的scipt.js代碼

這是編譯后的代碼scipt-transpiled.js

這個是source map 文件

這個mapping對應回轉換后的代碼就長這樣:


大家可以自行分析一下這個例子 。

以上,就是今天分享的source map 所有內容。

參考文檔:

https://medium.com/@trungutt/yet-another-explanation-on-sourcemap-669797e418ce



最近組建了一個江西人的前端交流群,如果你是江西人可以加我微信 ruochuan12 拉你進群。


點擊方卡片關注我、加個星標

一個愿景是幫助5年內前端人成長的公眾號

可加我個人微信?ruochuan12,長期交流學習

推薦閱讀

我在阿里招前端,該怎么幫你(可進面試群)

2年前端經驗,做的項目沒技術含量,怎么辦?

·················?若川簡介?·················

你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》多篇,在知乎、掘金收獲超百萬閱讀。

從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。

同時,活躍在知乎@若川,掘金@若川。致力于分享前端開發經驗,愿景:幫助5年內前端人走向前列。

今日話題

我經常推薦學會使用技術完成開發的同時也要多要研究原理。其實就是不停留在只會使用的層面,重基礎懂原理,知其然知其所以然。歡迎分享、收藏、點贊、在看我的公眾號文章~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/275748.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/275748.shtml
英文地址,請注明出處:http://en.pswp.cn/news/275748.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

OOP 中的 方法調用、接口、鴨式辯型、訪問者模式

2019獨角獸企業重金招聘Python工程師標準>>> 方法調用的四種方式 直接調用:通過類或者實例直接調用其方法。接口調用或者轉型調用:通過將實例回調給一個接口對象,或者轉型為一個父類的實例,來調用間接調用:…

Substitution控件MethodName無法取到Session的解決辦法

Substitution是對緩存頁面實現動態顯示部分內容的控件,使用方法:在后臺頁面添加靜態方法,private static string MethodName(HttpContext context),經典的用法是動態顯示的是當前的登錄用戶名字(非登錄用戶顯示其他的)…

java中想要保留2位小數_java使double保留兩位小數的多方法 java保留兩位小數

mport java.text.DecimalFormat;DecimalFormat df new DecimalFormat("######0.00");double d1 3.23456double d2 0.0;double d3 2.0;df.format(d1);df.format(d2);df.format(d3);3個結果分別為:3.230.002.00java保留兩位小數問題:方式一&#x…

游戲 新手引導 設計_我認為每個新手設計師都應該知道什么

游戲 新手引導 設計重點 (Top highlight)I should probably have titled this article “What I wish I knew as a newbie designer.” Anyway, I’ve been doing this graphic design thing for a little over a year now, and I know now, a few things that could have made…

畢業年限不長的前端焦慮和突破方法

大家好,我是若川。今天周六,分享一篇相對輕松的文章。經作者耳東蝸牛 授權轉載鏈接:https://juejin.cn/post/6968002742321152014也可點擊文末閱讀原文直達本篇文章來源于:周五和團隊成員[20年畢業]的一次閑聊。畢業不到一年&…

開源自然語言處理工具包hanlp中CRF分詞實現詳解

CRF簡介 CRF是序列標注場景中常用的模型,比HMM能利用更多的特征,比MEMM更能抵抗標記偏置的問題。 [gerative-discriminative.png] CRF訓練 這類耗時的任務,還是交給了用C實現的CRF。關于CRF輸出的CRF模型,請參考《CRF模型格式說明…

java 素數歐拉篩選_[C++]歐拉素數篩的理解與實現

在傳統的素數篩法中,我們使用了對于每一個數n,在 1~(√n) 范圍內進行取模檢查,這樣逐一判斷的復雜度為n(√n)。但如果我們需要更快的篩法時怎么辦?于是著名的歐拉篩誕生了。它能將復雜度降為**O(n)**級別。1.關鍵理解:…

交互規則_您必須永不中斷的10條交互設計規則

交互規則重點 (Top highlight)In life, there are certain rules you must never break. If you do there will be hell to pay. In User Interface design there are also rules to live by. They are called “heuristics” or general principles that improve usability in…

一個幫助我100%拿offer的面試學習法

大家好,我是若川。今天周日,再分享一篇相對輕松的文章。文中說的面試學習法有一定的借鑒意義。另外我也推薦大家每隔一段時間不為跳槽的更新自己簡歷,也是對自己一階段的梳理總結,畢竟功在平時。哈嘍大家好,我是大圣&a…

2010年終總結

還有兩天2010就要結束了,寫下自己的年終總結吧,以總結自己,展望明年。2010對我來說是怎樣的一年呢?忙碌的一年,鴨梨更大的一年,折騰的一年,復雜的一年,夢游的一年,痛并快…

java獲取apk啟動activity_兼容 Android 10 啟動 APK 實現方案

背景我們想啟動 APK 程序,有很多種方法,可以使用 Intent,也可以使用 adb shell 命令來啟動,還有通過反射來啟動 APk 程序。我們這里主要討論通過反射的方式來啟動 apk 程序。Android10 之前,我們通過反射來啟動 APK&am…

Android Studio中解決jar包重復依賴導致的代碼編譯錯誤

在原本的代碼中已經使用了OKHTTP和rxjava,然后今天依賴retrofit的時候一直報錯 Program type already present: okhttp3.internal.ws.RealWebSocket$1.class 說是我重復添加了OKHTTP的包,但其實我直接把OKHTTP的依賴注釋掉都沒用,只要依賴ret…

面試被問項目經驗不用慌,按這個步驟回答絕對驚艷

大家好,我是若川。常有小伙伴問,面試時項目經驗怎么回答,經常會分享這篇文章給TA。本文經授權轉載。面試、學習源碼系列、年度總結、JS基礎系列前言本篇文章的作者是來自阿里淘系用戶增長前端團隊的“亦遜”,18年作為雙非本科生通…

使用概念模型 和心智模型的_為什么要使用模型?

使用概念模型 和心智模型的In a former life, I studied critical feminist theory. This included the field of Semiotics — the study of signs and the production of meaning, as well as Deconstruction —the unpacking of meaning to question assumptions.在過去的生…

長效密鑰與臨時密鑰JAVA判斷_MSBuild無法使用臨時密鑰簽署ClickOnce清單(錯誤MSB3326和MSB3321)...

我正在嘗試在Windows Server計算機上構建ClickOnce Windows Forms項目(.NET 3.5 / Visual Studio 2010) . (為了使用Hudson CI自動化構建過程 . )為了對ClickOnce清單進行簽名,我在Visual Studio中創建了一個臨時密鑰 temp.pfx . 我可以在我的工作站上從Visual Stud…

URL some

** 路由系統:URL配置(URLconf)就像Django所支撐網站的目錄. 本質是URL與該URL要調用的函數的映射表 基本格式 : from django.conf.urls import url urlpatterns [url(正則表達式,views視圖,參數,別名) ] 參數 -- 傳給函數視圖的默認參數 (字典形式) 別名 -- 一個可選的name參…

什么?在 VSCode 里也能用 Postman了?

大家好,我是若川。VSCode中有很多好用的插件,今天推薦 Postcode。面試、學習源碼系列、年度總結、JS基礎系列以前一直在用postman做API測試,如果你同時在使用vscode開發時,每次切出去可能比較煩,其實就是太懶了。。。作…

根據窗口名稱查找關鍵字彈性域用到的表,列等信息

/*根據窗口名稱查找關鍵字彈性域用到的表,列等信息*/--selectc.id_flex_name, a.id_flex_structure_name, b.form_left_prompt, c.application_table_name, b.application_column_name, b.flex_value_set_id fromfnd_id_flex_struct…

英語 動畫 教學 字母_字母形式在閱讀教學中的作用

英語 動畫 教學 字母Note: this essay may also be found on Design Observer.注意:這篇文章也可以在 Design Observer 上找到 。 My first-grade reading tutor gave the best stickers. Puffy, smelly, sparkly — she even had a few that were fuzzy. At that …

java中自定義表單和流程_讓馳騁工作流程引擎 ccbpm使用自定義表單來實現自己的業務邏輯....

1.1.1.1: SDK表單概要說明:我們把流程引擎與表單引擎統稱為ccbpm,但是有一些用戶并不想使用表單引擎,而是用自己的表單,僅僅使用流程引擎,這樣的方式就要采用ccbpm的sdk表單開發模式。關于ccbpm的SDK:ccbpm的sdk就是cc…