<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          聊一聊SourceMap

          共 5303字,需瀏覽 11分鐘

           ·

          2022-03-09 12:49

          ????

          前言

          我們項目的代碼在經(jīng)過編譯打包后,會將開發(fā)時多個文件的代碼合并到同一份文件中,而且還會經(jīng)過各種壓縮,合并,代碼丑化等等操作,轉(zhuǎn)換完最終生成的代碼才會用于線上環(huán)境,所以我們線上實際運行的代碼跟我們開發(fā)時的代碼是有非常大的不同,如果此時出現(xiàn)了bug,那么我們只能定位到轉(zhuǎn)換后代碼的位置,但此時的代碼已經(jīng)面目全非了

          轉(zhuǎn)換后的代碼類似下面這樣

          雖然這種代碼對計算機非常友好,但是我們debug將會變得很困難,這時候就需要sourcemap了

          什么是SourceMap

          簡單來說,Sourcemap 就是一個信息文件,它里面存儲著代碼轉(zhuǎn)換前后的對應位置信息,也就是轉(zhuǎn)換壓縮后的代碼所對應的轉(zhuǎn)換前的源代碼位置,是源代碼和生產(chǎn)代碼的映射, Sourcemap 解決了在打包過程中,代碼經(jīng)過壓縮,去空格以及 babel 編譯轉(zhuǎn)化后,由于代碼之間差異性過大,debug 困難的問題

          大家的項目在開發(fā)完進行build后,在打包文件夾里除了有js,css,圖片等資源,一定還見過 .js.map文件,這種就是sourcemap文件

          點開一個打包后的js文件,拉到最后一行,可以看到 //# sourceMappingURL=main.js.map

          有了這行,就可以啟用sourcemap,這個sourceMappingURL就是標記了該文件的sourcemap的地址,這個sourcemap文件可以放在本地,也可以放在網(wǎng)絡上

          再點開一個 .js.map 文件看看,有一堆類似亂碼的東西,后面看一個簡單一點的

          SourceMap的生成

          生成的方法有很多,而且有很多前端的工具都支持,如webpack,uglifyjs,gulp等,這里就不詳細講述了,對怎么生成sourcemap感興趣可以看這個文章

          https://code.tutsplus.com/tutorials/source-maps-101--net-29173

          SourceMap的屬性

          我們看一個簡單一點的代碼

          const?value?=?123;
          console.log(value);

          用webpack打包后的代碼

          console.log(123);
          //#?sourceMappingURL=bundle.js.map

          生成的sourcemap文件

          {
          ???version?:?3,
          ???file?:??bundle.js?,
          ???mappings?:??AACAA,QAAQC,IADM?,
          ???sources?:?[
          ?????webpack://studysourcemap/./test.js?
          ??],
          ???sourcesContent?:?[
          ?????const?value?=?123;\nconsole.log(value);?
          ??],
          ???names?:?[
          ?????console?,
          ?????log?
          ??],
          ???sourceRoot?:???
          }

          每個屬性的含義如下

          • version:遵循的是哪一個sourcemap版本的規(guī)范(下面會淺提一下)
          • sources:轉(zhuǎn)換前的源文件url數(shù)組(數(shù)組是因為存在多個文件合并的情況)
          • names:在mappings中引用的標識符數(shù)組(可以理解為轉(zhuǎn)換前代碼的所有變量名和屬性名)
          • sourceRoot:源文件的根路徑
          • sourcesContent:轉(zhuǎn)換前源文件的原始內(nèi)容,也是一個數(shù)組
          • mappings:記錄源碼和編譯后代碼的位置信息的base64 VLQ 字符串,是最重要的內(nèi)容
          • file:生成的與該sourcemap文件關聯(lián)的文件名,也就是打包編譯后的文件名

          SourceMap的版本

          關于sourcemap的版本

          • 2009年,google介紹他的一個編譯器Cloure Compiler時,也順便推出了一個調(diào)試插件Closure Inspector,可以方便調(diào)試編譯后的代碼,這個就是 sourcemap 的雛形
          • 2010年,Closure Compiler Source Map 2.0中,共同制定了一些標準,已決定使用base64編碼,但是生成的map文件要比現(xiàn)在大很多
          • 2011年,第三代出爐, Source Map Revision 3 Proposal,也就是我們現(xiàn)在用的 sourcemap 的版本,這也就是為什么我們上面map文件的 version=3 了,這一版對算法進行了優(yōu)化,大大縮小了map文件的體積

          第一版生成的map文件大概有轉(zhuǎn)化后文件的10倍大,第二版則將體積減少了20%~30%,第三版又在v2的基礎上體積減少了一半

          正是因為有了第三代 Source Map Revision 3 Proposal 這個標準,不同的打包工具和瀏覽器才能使用sourcemap,github上的一個根據(jù)這個標準生成sourcemap的庫 https://github.com/mozilla/source-map

          SourceMap的原理

          這里主要關注mappings和names屬性,mappings屬性是一個很長的字符串,它分成三個部分

          1. 分號(;),表示行對應,生成的文件的每一行用分號(;)分隔,一個分號代表轉(zhuǎn)換后源碼的一行
          1. 逗號(,),位置對應,每一段用逗號(,)分隔,一個逗號對應轉(zhuǎn)換后源碼的一個位置
          1. 英文字母,每一段由1,4或5塊可變長度的字段組成,記錄原始代碼的位置信息

          舉一個簡單的例子,假設有如下的mappings屬性

          ?mappings?:??AACAA;QAAQC,IADM?,

          有一個分號,說明有兩行代碼,分號前 AACAA 是第一行,后面 QAAQC,IADM 是第二行

          第二行有一個逗號,說明這一行分為兩段,QAAQCIADM

          分號跟逗號大家應該都沒什么疑問,主要就是英文字母這一塊的意義位置對應的原理

          每一段最多有5個部分

          • 第一部分,表示這個位置在(轉(zhuǎn)換后的代碼的)的第幾列
          • 第二部分,表示這個位置屬于sources屬性中的哪一個文件
          • 第三部分,表示這個位置屬于轉(zhuǎn)換前代碼的第幾行
          • 第四部分,表示這個位置屬于轉(zhuǎn)換前代碼的第幾列
          • 第五部分,表示這個位置屬于names屬性中的哪一個變量

          那么這五個部分是怎么來的,我們一步一步來看

          假設文件a.js有一行代碼 I Love SourceMap,最終打包后輸出的文件為bundle.js,內(nèi)容為 Javascript is awesome,如下

          image.png

          那么怎么表示映射關系

          以 Love 為例,它原始的位置為(0,2),輸出后是awesome,位置為(0,14),那么我們可以這樣來映射

          image.png

          像這樣寫成一種固定的格式,里面包含了原始位置和輸出后的位置,單詞,同時還有原始文件名,因為可能把多個文件進行處理輸出,如果不寫文件名,就不知道輸入位置來自哪個文件

          輸出后的單詞映射關系
          Javascript0|0|a.js|0|7|SourceMap
          is0|11|a.js|0|0|I
          awesome0|14|a.js|0|2|Love

          我們可以優(yōu)化一下,把a.js和最后面的單詞提出來各放到一個數(shù)組里,用sources記錄所有的原始文件名,names記錄原始文件中的所有單詞,然后用下標表示他們,以Love為例,就變成

          image.png

          很多時候,我們輸出的文件其實是只有一行的,所以可以把輸出文件的行號省略掉,就變成

          image.png

          考慮到,如果文件特別大的話,那么行列的數(shù)值可能會特別大,所以可以考慮用相對位置來代替絕對位置來表示,只用絕對位置表示第一個單詞的位置,后面的都使用相對前一個單詞的位置

          image.png
          原始單詞輸入位置輸出單詞輸出位置映射
          I(0,0) 絕對位置is(0,11)絕對位置11|0|0|0|0
          Love(0,2) 相對I的位置awesome(0,3)相對is的位置3|0|0|2|1
          SourceMap(0,5) 相對Love的位置Javascript(0,-14) 相對awesome的位置-14|0|0|5|2

          所以我們現(xiàn)在可以得到這么一個初步的map文件

          {
          ????names:?['I',?'Love',?'SourceMap'],
          ????sources:?['a.js'],
          ????mappings:?[11|0|0|0|0,?3|0|0|2|1,?-14|0|0|5|2]
          }

          但是mappings這里十分難看,而且還需要用來分隔,多占一個位置,用 vlq 編碼就可以解決分隔數(shù)字的問題,他的核心思路是在連續(xù)的數(shù)字上做標記,我們先來理解一下,拿上面 mappings 屬性的第一個為例,去掉,然后在連續(xù)的字符上加上一個標記

          110000

          從左往右開始讀取,數(shù)字1有標記,說明還有連續(xù),再取下一個,是1,這個1沒被標記,第一個數(shù)結束,所以第一個數(shù)是11

          繼續(xù)往下,0沒被標記,說明是一個完整的數(shù)字,第二個數(shù)就是0

          依此類推。。。

          最終就能得到11,0,0,0,0

          而 vlq 利用6位二進制數(shù)進行存儲,其中第一位就表示是否連續(xù)的標識位,最后一位表示正數(shù)還是負數(shù)(0正數(shù),1負數(shù)) ,中間只有4位,因此一個單元表示的范圍為[-15,15],如果超過了就要用連續(xù)標識位了

          看幾個例子來理解,每一步的變化我都用不同顏色標記了

          image.png

          第三步(按...5554分割),最右邊4位是因為他還需要額外多表示一位符號位,其余的都可以用5位來表示數(shù)值

          倒數(shù)第二步倒順序,是因為VLQ表示數(shù)據(jù)字節(jié)組的順序是倒過來的

          最終我們可以得到他們的 vlq 編碼

          十進制數(shù)值vlq編碼vlq編碼每一段對應的數(shù)值
          500101010
          -19100111 00000139和1

          然后再把它轉(zhuǎn)成base64編碼,可以查下面這張表

          就可以得到5和-19的base64 vlq編碼了,因為5的 vlq 編碼數(shù)值是10,所以查上表可得到K,同理-19可以得到n和B,最終能得到5和-19的 base64 vlq 編碼分別是K和nB

          這里有一個網(wǎng)站可以自己轉(zhuǎn)換驗證一下https://www.murzwin.com/base64vlq.html

          然后我們回過頭為我們最開始那個簡單的js文件手動生成一下map文件來驗證一下

          const?value?=?123;
          console.log(value);

          打包后的代碼

          console.log(123);
          //#?sourceMappingURL=bundle.js.map

          sources和names是可以先確定好的

          {
          ?????sources?:?[?a.js?],
          ?????names?:?[?console?,??log?],
          }

          再得到 base64 vlq 編碼


          原始位置輸出位置sources索引names索引映射每一部分的vlq編碼base64 vlq編碼
          console(1,0)絕對(0,0)絕對00001
          log(0,8)相對(0,8)相對01800
          123(-1,6)相對(0,4)相對040-1

          所以我們可以得到最終的map文件

          {
          ?????sources?:?[?a.js?],
          ?????names?:?[??console?,??log?],
          ?????mappings?:??AACAA,QAAQC,IADM?,
          ????//?...其他的
          }

          反過來也能根據(jù)sourcemap文件推出原始的位置,這里就不再演示了

          SourceMap總結

          • 映射轉(zhuǎn)換過后的代碼和源代碼之間的關系
          • 代碼中引入 //# sourceMappingURL=xxx.js.map 啟用
          • source Map 解決了源代碼和運行代碼不一致所產(chǎn)生的問題
          • 不只是js文件有,css文件也有
          • 核心原理是 base64 vlq 編碼

          ???H5-Dooring,讓H5制作更簡單

          感謝巨人

          https://juejin.cn/post/7023537118454480904

          https://juejin.cn/post/6963076475020902436

          https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.1ce2c87bpj24

          https://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html

          https://www.html5rocks.com/en/tutorials/developertools/sourcemaps/

          http://www.qiutianaimeili.com/html/page/2019/05/89jrubx1soc.html

          ???

          便內(nèi)^_^

          ??~

          瀏覽 110
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人肏屄| 亚洲欧美人妻 | 欧洲超清一区二区三区视频 | 日逼片 | 7777久久久久亚洲精品 |