电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

分享一款超好用的JavaScript 打包壓縮工具

瀏覽:6日期:2023-06-21 14:52:12

背景

平時大家在開發 Js 項目的時候,可能已經離不開 webpack 等打包工具了。而 webpack 打包速度大概就是“能用“的水平。大概去年開始,我就開始在構想,如果能寫一個極速的打包工具,功能未必需要很強,可能對小項目非常有用。去年我用 C++ 寫完 parser 之后,便沒什么動力寫下去了。但是最近發現有這個想法的不止我一個,Figma 的 CTO 業余之際寫了一個打包器 https://github.com/evanw/esbuild ,可以說完完全全實現了我想象中的需求,不過他是用 Go 語言實現的。我看到這個項目時心里一想,這不是我去年就想做的事嗎,這 push 我趕緊把打包壓縮部分完成。

代碼

Github 地址: https://github.com/vincentdchan/jetpack.js

優化思路

并行 Parsing

毫無疑問,每一個 js 文件的 parsing 可以在不同線程完成,這就需要支持并行的語言。由于 parsing 的結果是 AST,所以需要可以共享內存的語言(排除通過 messeage parsing 實現多線程的語言)。滿足以上兩個要求的語言不多。 Evan 選擇了 Go,我選擇了 C++。

減少遍歷次數

要想速度快,就要減少 AST 的遍歷次數。最好就是只遍歷一次來生成代碼,在 Parsing 構建 AST 的時候就收集足夠的信息。但是這也意味著只能做比較淺層次的優化,不能做深層次的壓縮(死代碼消除,tree shaking 都做不了)。

架構

由上述思路我總結出了以下打包的架構:

并行 parse 文件 作用域提升、生成框架代碼、重命名變量 并行生成代碼 合并輸出文件

流程圖如下:

分享一款超好用的JavaScript 打包壓縮工具

打包壓縮原理

本章節主要講如何“最簡單“地壓縮 Js 代碼。本章節假設讀者對編譯原理有一定了解,知道什么是 AST。如果不懂請直接跳到下文「性能」章節。

字面量替換

字面替換最簡單。規則有一下幾個:

undefined 替換為 void 0 true 替換為 !0 , false 替換為 !1

:warning: 注意:在 ES 中,undefined 是標識符(Identifier),而不是關鍵字,也就是說你可以定義一個叫 undefined 的變量,所以這個時候不能簡單地替換為 void 0

常量折疊

計算簡單的運算:

var two = 1 + 1;var foobar = ’foo’ + ’bar’;

轉換成

var two = 2;var foobar = ’foobar’;

:warning: 注意:這里要注意實現的平臺和 js 的差異,比如在 C++ 里面大整數相加可能會溢出,而在 Js 會自動轉換成 bigint. 加法問題就如此,其他運算符問題更多。如果要完整實現常量折疊,可能要部分實現 js 引擎。

變量別名

別名就是要給變量重新賦予比較短的變量名。從字母一直排上去,abcd,一個字母用完了用兩個字母。實現起來也很簡單,用一個計數器,一直加上去就可。最后每個變量分配一個數字,把這個數字映射到相應的英文字母上,有點像 36 進制轉換成字母的面試題。不過這里有一點值得注意的是,變量名第一個字母不能是數字,第二個字母開始可以是數字,要考慮到這一點,才能盡可能“壓榨”變量名。

為了盡可能地“壓榨”變量名,同一級的作用域里面的變量名是可以使用相同的變量名。到下一級的時候,對子作用域進行合并。

舉個例子:

function Mother() {var e = ’capture’; // d 不能使用跟子作用域同樣的變量名,不然子作用域無法捕獲這個變量function A(a, b, c, d) { console.log(e);}function B(a, b, c) { // B 跟 A 函數同級,分配同樣的變量名 // ...}}

上述例子中,A 和 B 都沒有子作用域了,變量名從 0 開始分配。到給 Mother 下 e 分配變量名時,找到子作用域最大的計數器。分配最多的子作用域 A 分配了 4 個,所以 B 計數器從 5 開始分配,所以給 e 分配了5,所以 e 就得到了這個名字。

所以變量別名就是從 AST 的葉子開始向上構造,一直分配到根結點把所有作用域都分配完為止。

小技巧

這里 esbuild 采用了比較聰明的技巧。它統計了所有變量的引用次數,然后進行排序,引用次數最多的變量分配到的名字就是盡量短的,這樣也可以減少編譯出來 js 的體積。我在寫 jetpack 打包的時候,也借鑒了這種做法。

模塊合并

模塊合并的辦法有很多。webpack 采用的是用 function 把每個函數包起來,放到了一個長長的數組里面,然后實現了自己的 require,esbuild 也采用了類似的方法。

Rollup.js 實現的方法則是作用域提升(Scope hoisting),把模塊都放到根作用域。這里我采用的方法也是作用域提升。

假設有 a.js 文件:

export function A() { console.log(’a’);}

然后有 main.js 文件:

import { A as ExternalA } from ’./a’;function A() { console.log(’local A’);}export function main() { A() + ExternalA();}

使用 jetpack 打包完的結果:

// a.jsfunction A() { console.log(’a’);}// main.jsfunction A_0() { console.log(’local A’);}function main() { A_0() + A();}export { main };

難點在于作用域合并。實際上在 ES modules 里面不同 modules 之間引用是一個圖結構。

C++ 的優化

除了策略上的優化,C++ 還提供了諸多基礎數據結構/內存方面的優化。

shared_ptr

AST 的結點全部使用 shared_ptr,有人可能認為這是一個很大的開銷。但是早期的時候我實現過一個裸指針版本(不釋放內存),并沒有測出有明顯差距。

使用 shared ptr 很重要一個原因是,一個子樹可能被其他類擁有(打包模塊,Scope,ES Module 管理器)。這個時候如果用 unique ptr 的話就會 gg。只能說 GC 大法好。

對于 C++ 這種沒有 GC 的語言有一個毛病就是:析構 AST 非常耗時。AST 夠大的話能耗上十幾 ms(這個時間跟 gc 比有何優勢?),所以因此我也能想出了一個辦法: 不釋放內存 ……。

最后說一句: GC 大法好

robin hood hashing

由于打包器中大量使用哈希表,所以提高哈希表速度尤其重要,這里我使用了 robin hood hashing

參見: https://martin.ankerl.com/2019/04/01/hashmap-benchmarks-01-overview/

在 hash 方面我有一個設想,就是像 Lua 一樣,對于短字符,在字符串創建的時候把 hash 記下來,這樣在多次使用哈希表的時候可以節省 hash 的時間(但是要求字符串是 immutable 的)。為此我專門寫了個 String 類,最后的結果是總體速度慢了 2-3x,測出來是 immutable 字符串拼接耗時太多,最后放棄了這個方案。

jemalloc

Parsing 過程中需要大量分配 node,大家都知道很明顯 C++ 的 new 并不夠快。經過測試在 macOS 下使用 jemalloc 會讓 parsing 速度提升 1 倍。使 用系統 malloc 會導致 parsing 速度比 Go 慢 1x 左右,慢在 new 。

當然了,內存池我也試過的,測出來速度基本和 jemalloc 一樣,所以就直接用 jemalloc 了。

性能

分享一款超好用的JavaScript 打包壓縮工具

總結

寫編譯器需要快速大量產生 node 結點,大量樹和圖的結構,這一方面的運算 C++ 并沒有什么優勢可言。

不得不承認,使用 C++ 你要思考很多東西,做很多很多額外的工作,才能獲得比 Go 還快的速度(什么都不想做出來只會比 Go 還慢)。另一方面使用 C++ 會讓你額外考慮很多和業務無關的東西,大大降低開發速度,而對于打包器這個場景 C++ 在這一塊本身不能提供很大優勢。

到此這篇關于寫一個飛快的 JavaScript 打包壓縮工具的文章就介紹到這了,更多相關JavaScript 打包壓縮工具內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: JavaScript
相關文章:
主站蜘蛛池模板: 光伏支架成型设备-光伏钢边框设备-光伏设备厂家 | 飞歌臭氧发生器厂家_水处理臭氧发生器_十大臭氧消毒机品牌 | 钢制拖链生产厂家-全封闭钢制拖链-能源钢铝拖链-工程塑料拖链-河北汉洋机械制造有限公司 | 煤棒机_增碳剂颗粒机_活性炭颗粒机_木炭粉成型机-巩义市老城振华机械厂 | 气胀轴|气涨轴|安全夹头|安全卡盘|伺服纠偏系统厂家-天机传动 | 招商帮-一站式网络营销服务|互联网整合营销|网络推广代运营|信息流推广|招商帮企业招商好帮手|搜索营销推广|短视视频营销推广 | led全彩屏-室内|学校|展厅|p3|户外|会议室|圆柱|p2.5LED显示屏-LED显示屏价格-LED互动地砖屏_蕙宇屏科技 | 甲级防雷检测仪-乙级防雷检测仪厂家-上海胜绪电气有限公司 | 防渗膜厂家|养殖防渗膜|水产养殖防渗膜-泰安佳路通工程材料有限公司 | 无压烧结银_有压烧结银_导电银胶_导电油墨_导电胶-善仁(浙江)新材料 | 成都装修公司-成都装修设计公司推荐-成都朗煜装饰公司 | 首页-浙江橙树网络技术有限公司 石磨面粉机|石磨面粉机械|石磨面粉机组|石磨面粉成套设备-河南成立粮油机械有限公司 | 3d打印服务,3d打印汽车,三维扫描,硅胶复模,手板,快速模具,深圳市精速三维打印科技有限公司 | 磁力轮,磁力联轴器,磁齿轮,钕铁硼磁铁-北京磁运达厂家 | 武汉印刷厂-不干胶标签印刷厂-武汉不干胶印刷-武汉标签印刷厂-武汉标签制作 - 善进特种标签印刷厂 | 国资灵活用工平台_全国灵活用工平台前十名-灵活用工结算小帮手 | 塑料瓶罐_食品塑料瓶_保健品塑料瓶_调味品塑料瓶–东莞市富慷塑料制品有限公司 | 纯水电导率测定仪-万用气体检测仪-低钠测定仪-米沃奇科技(北京)有限公司www.milwaukeeinst.cn 锂辉石检测仪器,水泥成分快速分析仪-湘潭宇科分析仪器有限公司 手术室净化装修-手术室净化工程公司-华锐手术室净化厂家 | Safety light curtain|Belt Sway Switches|Pull Rope Switch|ultrasonic flaw detector-Shandong Zhuoxin Machinery Co., Ltd | 螺旋绞龙叶片,螺旋输送机厂家,山东螺旋输送机-淄博长江机械制造有限公司 | 聚合氯化铝_喷雾聚氯化铝_聚合氯化铝铁厂家_郑州亿升化工有限公司 | 分光色差仪,测色仪,反透射灯箱,爱色丽分光光度仪,美能达色差仪维修_苏州欣美和仪器有限公司 | 影合社-影视人的内容合作平台| 正压送风机-多叶送风口-板式排烟口-德州志诺通风设备 | 播音主持培训-中影人教育播音主持学苑「官网」-中国艺考界的贵族学校 | 车辆定位管理系统_汽车GPS系统_车载北斗系统 - 朗致物联 | 浙江皓格药业有限公司| 定制奶茶纸杯_定制豆浆杯_广东纸杯厂_[绿保佳]一家专业生产纸杯碗的厂家 | 考勤系统_考勤管理系统_网络考勤软件_政企|集团|工厂复杂考勤工时统计排班管理系统_天时考勤 | PTFE接头|聚四氟乙烯螺丝|阀门|薄膜|消解罐|聚四氟乙烯球-嘉兴市方圆氟塑制品有限公司 | 深圳3D打印服务-3D打印加工-手板模型加工厂-悟空打印坊 | 艺术生文化课培训|艺术生文化课辅导冲刺-济南启迪学校 | 深圳网站建设-高端企业网站开发-定制网页设计制作公司 | 不锈钢钢格栅板_热浸锌钢格板_镀锌钢格栅板_钢格栅盖板-格美瑞 | 山东石英砂过滤器,除氟过滤器「价格低」-淄博胜达水处理 | 书信之家_书信标准模板范文大全 最新范文网_实用的精品范文美文网 | 房在线-免费房产管理系统软件-二手房中介房屋房源管理系统软件 | 空调风机,低噪声离心式通风机,不锈钢防爆风机,前倾皮带传动风机,后倾空调风机-山东捷风风机有限公司 | 压力变送器-上海武锐自动化设备有限公司 | 航空连接器,航空插头,航空插座,航空接插件,航插_深圳鸿万科 | 阳光模拟试验箱_高低温试验箱_高低温冲击试验箱_快速温变试验箱|东莞市赛思检测设备有限公司 |