javascript 代碼是如何被壓縮的示例代碼
隨著前端的發(fā)展,特別是 React , Vue 等構(gòu)造單頁(yè)應(yīng)用的興起,前端的能力得以很大提升,隨之而來(lái)的是項(xiàng)目的復(fù)雜度越來(lái)越大。 此時(shí)的前端的靜態(tài)資源也越來(lái)越龐大,而毫無(wú)疑問(wèn) javascript 資源已是前端的主體資源,對(duì)于壓縮它的體積至為重要。
為什么說(shuō)更小的體積很重要呢:更小的體積對(duì)于用戶體驗(yàn)來(lái)說(shuō)意味著更快的加載速度以及更好的用戶體驗(yàn),這也能早就企業(yè)更大的利潤(rùn)。另外,更小的體積對(duì)于服務(wù)器來(lái)說(shuō)也意味更小的帶寬以及更少的服務(wù)器費(fèi)用。
前端構(gòu)建編譯代碼時(shí),可以使用 webpack 中的 optimization.minimizer 來(lái)對(duì)代碼進(jìn)行壓縮優(yōu)化。但是我們也需要了解如何它是壓縮代碼的,這樣當(dāng)在生產(chǎn)環(huán)境的控制臺(tái)調(diào)試代碼時(shí)對(duì)它也有更深刻的理解。
如何查看資源的體積
對(duì)于我們所編寫(xiě)的代碼,它在操作系統(tǒng)中是一個(gè)文件,根據(jù)文件系統(tǒng)中的 stat 信息我們可以查看該文件的大小。
stat 命令用來(lái)打印文件系統(tǒng)的信息:
$ stat config.js File: ‘config.js' Size: 3663 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 806060 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2020-02-13 13:43:54.851381702 +0800 Modify: 2020-02-13 13:43:52.668417641 +0800 Change: 2020-02-13 13:43:52.691417262 +0800 Birth: -
stat 打印的信息過(guò)大,如果只用來(lái)衡量體積,可以使用 wc -c
$ wc -c config.js 3663 config.js
如何壓縮代碼體積?
去除多余字符: 空格,換行及注釋
// 對(duì)兩個(gè)數(shù)求和
function sum (a, b) {
return a + b;
}
先把一個(gè)抽象的問(wèn)題給具體化,如果是以上一段代碼,那如何壓縮它的體積呢:
此時(shí)文件大小是 62 Byte , 一般來(lái)說(shuō)中文會(huì)占用更大的空間。
多余的空白字符會(huì)占用大量的體積,如空格,換行符,另外注釋也會(huì)占用文件體積。當(dāng)我們把所有的空白符合注釋都去掉之后,代碼體積會(huì)得到減少。
去掉多余字符之后,文件大小已經(jīng)變?yōu)?30 Byte 。 壓縮后代碼如下:
function sum(a,b){return a+b}
替換掉多余字符后會(huì)有什么問(wèn)題產(chǎn)生呢?
有,比如多行代碼壓縮到一行時(shí)要注意行尾分號(hào)。這就需要通過(guò)以下介紹的 AST 來(lái)解決。
壓縮變量名:變量名,函數(shù)名及屬性名
function sum (first, second) {
return first + second;
}
如以上 first 與 second 在函數(shù)的作用域中,在作用域外不會(huì)引用它,此時(shí)可以讓它們的變量名稱(chēng)更短。但是如果這是一個(gè) module 中, sum 這個(gè)函數(shù)也不會(huì)被導(dǎo)出呢?那可以把這個(gè)函數(shù)名也縮短。
// 壓縮: 縮短變量名
function sum (x, y) {
return x + y;
}
// 再壓縮: 去除空余字符
function s(x,y){return a+b}
在這個(gè)示例中,當(dāng)完成代碼壓縮 ( compress ) 時(shí),代碼的混淆 ( mangle ) 也捎帶完成。 但此時(shí)縮短變量的命名也需要 AST 支持,不至于在作用域中造成命名沖突。
更簡(jiǎn)單的表達(dá):合并聲明以及布爾值簡(jiǎn)化
合并聲明的示例如下:
// 壓縮前 const a = 3; const b = 4; // 壓縮后 const a = 3, b = 4;
布爾值簡(jiǎn)化的示例如下:
// 壓縮前 !b && !c && !d && !e // 壓縮后 b||c||d||e
這個(gè)示例更是需要解析 AST 了
AST
AST ,抽象語(yǔ)法樹(shù),js 代碼解析后的最小詞法單元,而這個(gè)過(guò)程就是通過(guò) Parser 來(lái)完成的。
那么 AST 可以做什么呢?
- eslint: 校驗(yàn)?zāi)愕拇a風(fēng)格
- babel: 編譯代碼到 ES 低版本
- taro/mpvue: 各種可以多端運(yùn)行的小程序框架
- GraphQL: 解析客戶端查詢(xún)
我們?cè)谌粘9ぷ髦薪?jīng)常會(huì)不經(jīng)意間與它打交道,如 eslint 與 babel ,都會(huì)涉及到 js 與代碼中游走。不同的解析器會(huì)生成不同的 AST,司空見(jiàn)慣的是 babel 使用的解析器 babylon ,而 uglify 在代碼壓縮中使用到的解析器是 UglifyJS 。
你可以在 AST Explorer [3] 中直觀感受到,如下圖:

那壓縮代碼的過(guò)程:code -> AST -> (transform)一顆更小的 AST -> code,這與 babel 和 eslint 的流程一模一樣。
UglifyJS
不要重復(fù)造輪子!
于是我找了一個(gè)久負(fù)盛名的關(guān)于代碼壓縮的庫(kù): UglifyJS3 [4] ,一個(gè)用以代碼壓縮混淆的庫(kù)。那它是如何完成一些壓縮功能的,比如替換空白符,答案是 AST。
webpack 中內(nèi)置的代碼壓縮插件就是使用了它,它的工作流程大致如下:
// 原始代碼 const code = `const a = 3;` // 通過(guò) UglifyJS 把代碼解析為 AST const ast = UglifyJS.parse(code); ast.figure_out_scope(); // 轉(zhuǎn)化為一顆更小的 AST 樹(shù) compressor = UglifyJS.Compressor(); ast = ast.transform(compressor); // 再把 AST 轉(zhuǎn)化為代碼 code = ast.print_to_string();
而當(dāng)你真正使用它來(lái)壓縮代碼時(shí),你只需要面向配置編程即可,文檔參考 uglify 官方文檔 [5]
{
{
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
}
}
在 webpack 中壓縮代碼
在知道代碼壓縮是怎么完成的之后,我們終于可以把它搬到生產(chǎn)環(huán)境中去壓縮代碼。終于到了實(shí)踐的時(shí)候了,雖然它只是簡(jiǎn)單的調(diào)用 API 并且調(diào)調(diào)參數(shù)。
一切與性能優(yōu)化相關(guān)的都可以在 optimization 中找到, TerserPlugin 是一個(gè)底層基于 uglifyjs 的用來(lái)壓縮 JS 的插件。
optimization: {
minimize: isEnvProduction,
minimizer: [
new TerserPlugin({
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
sourceMap: true
})
]
}
參考資料 [1]
shfshanyue/blog: https://github.com/shfshanyue/blog
[2]
前端工程化系列: https://github.com/shfshanyue/blog/tree/master/frontend-engineering
[3]
AST Explorer: https://astexplorer.net/
[4]
UglifyJS3: https://github.com/mishoo/UglifyJS2
[5]
uglify 官方文檔: https://github.com/mishoo/UglifyJS2#parse-options
到此這篇關(guān)于javascript 代碼是如何被壓縮的的文章就介紹到這了,更多相關(guān)js代碼壓縮內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用js模擬類(lèi)繼承小例子,學(xué)習(xí)js面向?qū)ο蟮呐笥芽梢詤⒖枷隆?/div> 2010-07-07
Babylon使用麥克風(fēng)并處理常見(jiàn)問(wèn)題解決
這篇文章主要為大家介紹了Babylon使用麥克風(fēng)并處理常見(jiàn)問(wèn)題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
微信小程序 列表的上拉加載和下拉刷新的實(shí)現(xiàn)
本文主要介紹了微信小程序中實(shí)現(xiàn)列表的上拉加載和下拉刷新的方法。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04
javascript寫(xiě)的一個(gè)鏈表實(shí)現(xiàn)代碼
今天在百度上看到有人問(wèn)怎么用Javascript 寫(xiě)一個(gè)學(xué)生管理系統(tǒng)。個(gè)人認(rèn)為沒(méi)有什么實(shí)現(xiàn)價(jià)值。無(wú)聊練練手吧,很久沒(méi)寫(xiě)JS了。2009-10-10
JS實(shí)現(xiàn)網(wǎng)頁(yè)時(shí)鐘特效
這篇文章主要為大家詳細(xì)介紹了JS實(shí)現(xiàn)網(wǎng)頁(yè)時(shí)鐘特效,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03
解決微信小程序云開(kāi)發(fā)中獲取數(shù)據(jù)庫(kù)的內(nèi)容為空的方法
這篇文章主要介紹了解決微信小程序云開(kāi)發(fā)中獲取數(shù)據(jù)庫(kù)的內(nèi)容為空的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05
JS原型prototype和__proto__用法實(shí)例分析
這篇文章主要介紹了JS原型prototype和__proto__用法,結(jié)合實(shí)例形式分析了JS原型prototype和__proto__使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2020-03-03
uniapp組件之tab選項(xiàng)卡滑動(dòng)切換功能實(shí)現(xiàn)
這篇文章主要介紹了uniapp組件之tab選項(xiàng)卡滑動(dòng)切換功能實(shí)現(xiàn),本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-01-01
關(guān)于Mozilla瀏覽器不支持innerText的解決辦法
在各大瀏覽器中,除Mozilla瀏覽器外,幾乎都支持一個(gè)元素的屬性:innerText。我們可以通過(guò)它來(lái)快速獲取某個(gè)元素的內(nèi)的文本。2011-01-01最新評(píng)論

