摘要:文件就是記錄了從源代碼文件到壓縮文件的一個(gè)代碼對(duì)應(yīng)關(guān)系記錄表,通過(guò)壓縮文件和文件可以原原本本找出源代碼文件。
細(xì)說(shuō) js 壓縮、sourcemap、通過(guò) sourcemap 查找原始報(bào)錯(cuò)信息 1. js 壓縮
js 壓縮對(duì)前端開(kāi)發(fā)者來(lái)說(shuō)是一門(mén)必修課。
一般來(lái)說(shuō),壓縮 js 主要出于以下兩個(gè)目的:
減小代碼體積,加快前端資源加載速度
保護(hù)源代碼不被別人獲取
壓縮 js 使用的工具庫(kù):
UglifyJS2: 壓縮 es5
uglify-es: 壓縮 es6+
closure-compiler、closure-compiler-js: google 的 js 壓縮、優(yōu)化工具
壓縮 js 的主要過(guò)程:
移除無(wú)用代碼
混淆代碼中變量名稱(chēng)、函數(shù)名稱(chēng)等
預(yù)編譯代碼
對(duì)結(jié)構(gòu)進(jìn)行扁平化處理
1. 移除無(wú)用代碼去掉所有對(duì)解析引擎來(lái)說(shuō)無(wú)用的字符,包括空格、注釋、換行、沒(méi)有用的變量聲明、函數(shù)聲明等。
2. 混淆代碼中變量名稱(chēng)、函數(shù)名稱(chēng)等把一些局部變量名稱(chēng)、函數(shù)名稱(chēng)等用 a, b, ...、$1, $2, ...、_1, _2, ... 之類(lèi)的簡(jiǎn)略字符進(jìn)行替換,達(dá)到混淆的目的。
源代碼
(function () { var hello = "hi"; var print = function (str) { console.log(str); }; print(hello); })();
壓縮后的代碼(僅演示混淆功能)
(function () { var a = "hi"; var b = function (c) { console.log(c); }; b(a); })();3. 預(yù)編譯代碼
把不依賴(lài)外部環(huán)境的邏輯提前進(jìn)行運(yùn)算,并把運(yùn)算結(jié)果替換到相應(yīng)的源碼處,然后從源碼中移除這段邏輯。
源代碼
(function () { var hello = "hi" + " everyone, "; var count = 3 * 5; console.log(hello + count + " girls"); })();
壓縮后的代碼(僅演示預(yù)編譯功能)
(function () { var hello = "hi everyone, "; var count = 15; console.log(hello + count + " girls"); })();4. 對(duì)結(jié)構(gòu)進(jìn)行扁平化處理
對(duì)于 js 來(lái)說(shuō),嵌套越深,執(zhí)行越慢,對(duì)代碼進(jìn)行扁平化處理也是優(yōu)化代碼的一種方式。
源代碼
(function () { var say = { hello: function (str) { console.log("hello " + str); } }; say.hello("everyone"); })();
壓縮后的代碼(僅演示扁平化結(jié)構(gòu)功能)
!function(str){console.log("hello "+str)}("everyone");完整示例
源代碼
(function () { var say = { hello: function (str) { console.log("hello " + str); } }; say.hello("everyone"); })();
壓縮后的代碼
!function(l){console.log("hello "+l)}("50 girls");2. sourcemap
通常 js 壓縮后只有一行代碼,并且里面的變量名與函數(shù)名等都是混淆了的,這在實(shí)際運(yùn)行中會(huì)有一個(gè)問(wèn)題,就是 js 的報(bào)錯(cuò)信息將會(huì)失真,無(wú)法追蹤到是在源代碼哪一行哪一列報(bào)的錯(cuò)。
sourcemap 便是為了解決這個(gè)問(wèn)題而生的。
sourcemap 文件就是記錄了從源代碼文件到壓縮文件的一個(gè)代碼對(duì)應(yīng)關(guān)系記錄表,通過(guò)壓縮文件和 sourcemap 文件可以原原本本找出源代碼文件。
查看阮一峰老師的 JavaScript Source Map 詳解 了解 sourcemap 的原理與格式。
一般在壓縮 js 的過(guò)程中,會(huì)生成相應(yīng)的 sourcemap 文件,并且在壓縮的 js 文件末尾追加 sourcemap 文件的鏈接 //# sourceMappingURL=bundle-file-name.js.map。這樣,瀏覽器在加載這個(gè)壓縮 js 的時(shí)候,就知道還有一個(gè)相應(yīng)的 sourcemap 文件,也一并加載下來(lái),運(yùn)行的過(guò)程中如果 js 報(bào)錯(cuò),也會(huì)給出相應(yīng)源代碼的行號(hào)與列號(hào),而非壓縮文件的。
比如,對(duì)下面的源碼進(jìn)行壓縮:
(function () { var say = { hi: function () { console.log("hi"); } }; say.hello(); return say; })();
未加 sourcemap 文件時(shí),報(bào)錯(cuò)信息是:
加上 sourcemap 文件時(shí),報(bào)錯(cuò)信息是:
sourcemap 擴(kuò)展webpack 對(duì) sourcemap 做了擴(kuò)展,定義在 devtool 配置項(xiàng)中:
eval: 每個(gè)模塊都使用 eval() 執(zhí)行,并且都有 //@ sourceURL,構(gòu)建很快,但無(wú)法正確顯示行號(hào)
eval-source-map: 每個(gè)模塊使用 eval() 執(zhí)行,并且 source map 轉(zhuǎn)換為 DataUrl 后添加到 eval() 中,一般開(kāi)發(fā)模式中使用這種方式
cheap-eval-source-map: 類(lèi)似 eval-source-map,但只映射行,不映射列,并忽略源自 loader 的 source map,僅顯示轉(zhuǎn)譯后的代碼
cheap-module-eval-source-map: 類(lèi)似 cheap-eval-source-map,但會(huì)保留源自 loader 的 source map
inline-source-map: source map 轉(zhuǎn)換為 DataUrl 后添加到 bundle 中
cheap-source-map: 只映射行,不映射列,并忽略源自 loader 的 source map,僅顯示轉(zhuǎn)譯后的代碼
inline-cheap-source-map: inline-source-map 與 cheap-source-map 的結(jié)合
cheap-module-source-map: 類(lèi)似 cheap-module-eval-source-map,但不使用 eval() 執(zhí)行
inline-cheap-module-source-map: inline-source-map 與 cheap-module-source-map 的結(jié)合
source-map: 整個(gè) source map 作為一個(gè)多帶帶的文件生成,產(chǎn)品環(huán)境一般使用這種模式
hidden-source-map: 類(lèi)似 source-map,但不會(huì)把 //# sourceMappingURL=bundle-file-name.js.map 追加到壓縮文件后面
nosources-source-map: 類(lèi)似 source-map,但只有堆棧信息,沒(méi)有源碼信息
更詳細(xì)信息可以參考:
webpack devtool(英文)
webpack devtool(中文)
使用建議對(duì)于使用 webpack 來(lái)構(gòu)建項(xiàng)目,建議在開(kāi)發(fā)時(shí)使用 eval-source-map,產(chǎn)品環(huán)境使用 source-map。
因?yàn)橛脡嚎s文件與 sourcemap 文件是可以原原本本的找到源代碼的,所以,為了保護(hù)源代碼,可以這樣隱藏 sourcemap 文件:
在 web 服務(wù)器設(shè)置外部不能訪問(wèn) sourcemap 文件,只能內(nèi)部訪問(wèn)
直接把 sourcemap 文件存放到其他地方
3. 通過(guò) sourcemap 查找原始報(bào)錯(cuò)信息一般而言,在產(chǎn)品階段,我們會(huì)用 window.onerror 來(lái)捕獲 js 報(bào)錯(cuò),然后上報(bào)到服務(wù)器,以此來(lái)收集用戶(hù)使用時(shí)發(fā)生的 bug:
window.onerror = function(message, source, lineno, colno, error) { // message: 錯(cuò)誤信息 // source: 報(bào)錯(cuò)腳本的 url 地址 // lineno: 行號(hào) // colno: 列號(hào) // error: 錯(cuò)誤對(duì)象 // 上報(bào)必要的信息到服務(wù)器 }
但產(chǎn)品環(huán)境的代碼都是壓縮的,行號(hào)和列號(hào)都是失真的,所以就需要用 sourcemap 文件來(lái)找到錯(cuò)誤對(duì)應(yīng)源代碼的行號(hào)與列號(hào),以及其他的信息。
使用工具: mozilla/source-map
源代碼
(function () { var say = { hi: function () { console.log("hi"); } }; say.hello(); return say; })();
壓縮后報(bào)錯(cuò)信息
window.onerror = function(message, source, lineno, colno, error) { console.log(`message: ${message}`); console.log(`source: ${source}`); console.log(`lineno: ${lineno}`); console.log(`colno: ${colno}`); console.log(`error: ${error}`); } // message: Uncaught TypeError: e.hello is not a function // source: url/to/bundle.min.js // lineno: 1 // colno: 982 // error: TypeError: e.hello is not a function
通過(guò) source-map 查找原始報(bào)錯(cuò)信息
const fs = require("fs"); const SourceMap = require("source-map"); const { readFileSync } = fs; const { SourceMapConsumer } = SourceMap; const rawSourceMap = JSON.parse(readFileSync("path/to/js/map/file", "utf8")); SourceMapConsumer.with(rawSourceMap, null, consumer => { const pos = consumer.originalPositionFor({ line: 1, column: 982 }); console.log(pos); });
查找到的原始信息
{ source: "path/to/index.js", line: 8, column: 7, name: "hello" }
這樣,便找到了原始報(bào)錯(cuò)信息:
原始報(bào)錯(cuò)文件:path/to/index.js
原始報(bào)錯(cuò)行號(hào):8
原始報(bào)錯(cuò)列號(hào):7
原始對(duì)象名稱(chēng):hello
如此,便能一下子就找到錯(cuò)誤在哪里了。
更多用法,參考 mozilla/source-map
后續(xù)更多博客,查看 https://github.com/senntyou/blogs
作者:深予之 (@senntyou)
版權(quán)聲明:自由轉(zhuǎn)載-非商用-非衍生-保持署名(創(chuàng)意共享3.0許可證)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/99228.html
摘要:從到再到搭建編寫(xiě)構(gòu)建一個(gè)前端項(xiàng)目選擇現(xiàn)成的項(xiàng)目模板還是自己搭建項(xiàng)目骨架搭建一個(gè)前端項(xiàng)目的方式有兩種選擇現(xiàn)成的項(xiàng)目模板自己搭建項(xiàng)目骨架。使用版本控制系統(tǒng)管理源代碼項(xiàng)目搭建好后,需要一個(gè)版本控制系統(tǒng)來(lái)管理源代碼。 從 0 到 1 再到 100, 搭建、編寫(xiě)、構(gòu)建一個(gè)前端項(xiàng)目 1. 選擇現(xiàn)成的項(xiàng)目模板還是自己搭建項(xiàng)目骨架 搭建一個(gè)前端項(xiàng)目的方式有兩種:選擇現(xiàn)成的項(xiàng)目模板、自己搭建項(xiàng)目骨架。 ...
摘要:從到再到搭建編寫(xiě)構(gòu)建一個(gè)前端項(xiàng)目選擇現(xiàn)成的項(xiàng)目模板還是自己搭建項(xiàng)目骨架搭建一個(gè)前端項(xiàng)目的方式有兩種選擇現(xiàn)成的項(xiàng)目模板自己搭建項(xiàng)目骨架。使用版本控制系統(tǒng)管理源代碼項(xiàng)目搭建好后,需要一個(gè)版本控制系統(tǒng)來(lái)管理源代碼。 從 0 到 1 再到 100, 搭建、編寫(xiě)、構(gòu)建一個(gè)前端項(xiàng)目 1. 選擇現(xiàn)成的項(xiàng)目模板還是自己搭建項(xiàng)目骨架 搭建一個(gè)前端項(xiàng)目的方式有兩種:選擇現(xiàn)成的項(xiàng)目模板、自己搭建項(xiàng)目骨架。 ...
摘要:從到再到搭建編寫(xiě)構(gòu)建一個(gè)前端項(xiàng)目選擇現(xiàn)成的項(xiàng)目模板還是自己搭建項(xiàng)目骨架搭建一個(gè)前端項(xiàng)目的方式有兩種選擇現(xiàn)成的項(xiàng)目模板自己搭建項(xiàng)目骨架。使用版本控制系統(tǒng)管理源代碼項(xiàng)目搭建好后,需要一個(gè)版本控制系統(tǒng)來(lái)管理源代碼。 從 0 到 1 再到 100, 搭建、編寫(xiě)、構(gòu)建一個(gè)前端項(xiàng)目 1. 選擇現(xiàn)成的項(xiàng)目模板還是自己搭建項(xiàng)目骨架 搭建一個(gè)前端項(xiàng)目的方式有兩種:選擇現(xiàn)成的項(xiàng)目模板、自己搭建項(xiàng)目骨架。 ...
摘要:另外這樣的異常捕獲不能捕獲的異常錯(cuò)誤信息,這點(diǎn)需要注意。最終大致的流程圖如下結(jié)語(yǔ)前端異常捕獲與上報(bào)是前端異常監(jiān)控的前提,了解并做好了異常數(shù)據(jù)的收集和分析才能實(shí)現(xiàn)一個(gè)完善的錯(cuò)誤響應(yīng)和處理機(jī)制,最終達(dá)成數(shù)據(jù)可視化。 關(guān)于 微信公眾號(hào):前端呼啦圈(Love-FED) 我的博客:勞卜的博客 知乎專(zhuān)欄:前端呼啦圈 前言 Hello,大家好,又與大家見(jiàn)面了,這次給大家分享下前端異常監(jiān)控中需...
摘要:在中,設(shè)置了一些配置,代碼如下通過(guò)它的注釋?zhuān)覀兛梢岳斫馑谥信渲昧遂o態(tài)路徑本地服務(wù)器配置項(xiàng)等參數(shù)。下面還有一個(gè)的對(duì)象,它是在本地服務(wù)器啟動(dòng)時(shí),打包的一些配置,代碼如下其中包括模版文件的修改,打包完目錄之后的一些路徑設(shè)置,壓縮等。 前言 這段時(shí)間,算是空出手來(lái)寫(xiě)幾篇文章了。由于很久都沒(méi)有時(shí)間整理現(xiàn)在所用的東西了,所以,接下來(lái)會(huì)慢慢整理出一些文檔來(lái)記錄前段時(shí)間的工作和生活。 這篇文章的主...
閱讀 1600·2021-09-23 11:21
閱讀 2357·2021-09-07 10:13
閱讀 843·2021-09-02 10:19
閱讀 1140·2019-08-30 15:44
閱讀 1732·2019-08-30 13:18
閱讀 1920·2019-08-30 11:15
閱讀 1115·2019-08-29 17:17
閱讀 2024·2019-08-29 15:31