大家好,我是若川。持續組織了6個月源碼共讀活動,感興趣的可以點此加我微信 ruochuan12?參與,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》?包含20余篇源碼文章。歷史面試系列
2021 的年度盤點我們選擇了一個特別的形式,把時間范圍拉長到 10 年,梳理前端工具鏈里的 12 個重要的包的發布和版本更新時間,結合 npm 下載數據,看看前端的工具鏈在這十年間有怎樣的演變。
查看本圖的全部細節,請訪問:https://time.graphics/line/598790
前史:2009 - 2010
2009 年前,前端的工具鏈不由 JavaScript 編寫,功能也較為簡單,如 make、python、C# 等。2009 年起,前端工具鏈的前置要求被逐個滿足。
模塊定義:2009 年,CommonJS 模塊定義規范被提出;
語言:2009 年,ECMAScript 5 發布,JavaScript 標準更明確,功能更多;
執行環境:2009 年,Node.js 發布初始版本;
包管理:2010 年,npm 發布;
第一章:尋找抽象,2011 - 2015
2011 年起,時機成熟,前端工具鏈開始大量冒出。這個階段的主流工具鏈均在嘗試各種抽象,以合理表達前端的自動化處理流程:
任務:2012 年 Grunt 發布首版12,將處理過程定義為多個不同的任務,每個任務執行一個函數或插件;
文件流:2013 年 Gulp 發布首版,并快速在同年發布到了 3.0 正式版,在 Grunt 抽象任務的基礎上,Gulp 引入了流編程的概念,避免在執行復雜任務時,需要將編譯中間結果放在臨時文件夾的場景;
模塊依賴:2012 年 Webpack 發布首版,并于 2014 年發布 1.0 正式版,它通過分析模塊間依賴來決定編譯過程,將可擴展點抽象為 loader 和 plugin。
對于不同的子場景,也有其他工具:
Node.js 支持:2011 年,browserify 發布首版,允許在前端倉庫引入 Node.js 的部分 API,并實現跟其他庫類似的打包步驟;
國內前端開發:2013 年,百度開源了 FIS,針對國內前端的更常見需求進行了支持,包括 GBK 特性支持;
轉譯:2014 年,Babel 發布首版3,重心放在對 JavaScript 轉譯,使得尚在提案階段的語言特性能兼容。
注 1:部分包在發版 npm 前,會通過官網直接分發,本文所有包發版時間以 npm 為準,不統計其他渠道,下略。
注 2:“首版”即 npm 包首次發版時間,它可能小于 1.0.0,下略。
注 3:Babel 首版叫做 6to5,后改名為 Babel。
第二章:打包一切,2016 - 2019
2016 年,Webpack 的 npm 年下載量和 Gulp、Grunt 達到同一個數量級,意味著 Webpack 統治前端工具鏈的時代來臨。
工具鏈中,存在如下的三類發展軸向:
高封裝性:即配置內容簡單,不需要寫太多配置即可完成前端流程配置;
低復雜度:即工具的內部實現簡單,文檔友好度、插件書寫復雜度均受此特性影響;
強能力:即支持的功能集更多。
對比出現的這些工具庫,很難做到同時擁有三個特性,如 Webpack 復雜度較高,Grunt 能力較弱,Gulp 封裝性較低。
Webpack 最終能統治社區,離不開它的三個王牌能力:一切皆可打包、本地模塊熱加載(HMR)和按需加載。而 Webpack 的弱項是其配置的繁瑣和復雜,在這個階段出現的大部分新包,也是在犧牲了部分能力的前提下,去強化封裝性和簡化復雜度:
2013 年?,Parcel 發布首版,并在 2018 年發布 1.0 正式版,在當時它主打無配置啟動項目;
2015 年,Rollup 發布首版,并在 2019 年發布 1.0 正式版,它主打工具庫的打包,相比 Webpack 配置更簡單和輕量;
面臨其他工具庫的挑戰,Webpack 也在 2018 年發布 4.0 版,支持了無配置啟動項目。
注 4:從 npm 記錄而言,Parcel 首版發布于 2013 年,但是它大規模進入公共視野是在 2017 年。
第三章:性能優化,2020 至今
2020 年開始,我們觀察到工具鏈開始將重心放在了性能優化上:
2020 年,Webpack 5.0 發布,實現了多進程編譯以優化計算密集型任務,并強化了緩存機制;
2020 年,Snowpack 發布首版正式版,主打不打包項目依賴的模塊(Bundleless),而是直接依賴 CDN 提供的模塊文件,大大提升了本地環境運行速度;
2020 年,Vite 發布首版,同樣主打 Bundleless。
那么,這些性能優化是不是到了理論極限呢?我們可以從工具鏈里不同的任務類型分述:
CPU 密集型,如壓縮、轉譯等環節,可能的優化有:
使用更高效算法:優化空間小;
壓榨 V8 性能:如參考 Crankshaft Bailout 或 TurboFan Bailout;
多核并行計算:受到進程通信開銷制約;
使用其他語言實現?:受到跨語言通信制約;
I/O 密集型,包括:
文件讀寫:利用 bundleless 減少本地文件讀取量;此外 Node.js 默認異步 API 使得此類任務能足夠快,優化空間小;
進程通信:序列化/反序列化開銷較大,共享內存的 worker_threads 尚不穩定?,此外線程啟動有損耗;
跨語言通信:2018 年,napi-rs 1.0 發布,Node.js 調用 rust 有了更高效簡單的方式?;
GPU 密集型,前端場景較少?,包括:
機器學習:如使用 NVIDIA RAPIDS API 的 node-rapids;
圖像處理:如 GPU.js。
社區找到的突破口在 CPU 密集型任務上,使用 Go 或者 Rust 書寫計算密集型的部分任務:
2019 年,基于 Rust 實現的 SWC 發布首版,對標 Babel,顯著提升了性能;
2020 年,使用 go 實現的 esbuild 發布首版,相比 SWC 更聚焦于 TypeScript 和 JavaScript 的轉譯,性能更快;
2020 年,Vite 發布 2.0,使用 esbuild 實現了性能二次提升;
2020 年,Parcel 發布 2.0,基于 napi-rs 和 Rust 重新實現;
2020 年,rome 發布首版,在 2021 年也轉為基于 Rust 開發。
最后,我們看看 2021 年的 npm 包年下載量數據:
Babel 14 億,穩坐榜首;
Webpack 8億,位列其次;
新興的高性能打包降序依次是:esbuild 4800萬、Vite 860萬、Parcel 340萬、SWC 280萬、Snowpack 170萬;
老牌的打包工具降序依次是:Rollup 2億、Gulp 720萬、Grunt 350萬。
時局如何變遷,讓我們拭目以待。
注 5:JavaScript 的特性為 JIT、弱類型、動態類型,其執行效率相對其他 AOT、強類型、靜態類型的語言更低。
注 6:Node 12 起正式支持 worker_threads,但穩定性一直不高,如觸發 Access Violation。
注 7:對于 Rust,一開始有 neon 提供 js binding,但性能還不夠快。
注 8:因為 Node.js 標準包里不包含對 GPU 的封裝。另外,瀏覽器缺乏直接利用 GPU 的 API,直到 WebGPU 出現。
正文未提及信源:
CommonJS 溯源:https://arstechnica.com/information-technology/2009/12/commonjs-effort-sets-javascript-on-path-for-world-domination/
JavaScript 二十年:https://github.com/doodlewind/jshistory-cn
npm 包發版時間:https://libraries.io/
npm 包下載數據:https://npm-stat.com/charts.html
阿里媽媽前端快爆從 2017 年開始,已堅持更新到 2022 年。過去 5 年間,我們致力于傳達前端業界的更迭,也慢慢分化出來了重客觀信息的新聞版塊、重知識聚合的專題板塊、重碎片化分享的百寶箱板塊。
感謝全體編輯部成員過去一年的貢獻:一絲、墨塵、承虎、池冰。
感謝各位對本文的糾錯和支持:尤雨溪、doodlewind、工業聚、太狼、游真、E0、天豬、徐振東。
·················?若川簡介?·················
你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列》20余篇,在知乎、掘金收獲超百萬閱讀。
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,最近組織了源碼共讀活動,幫助3000+前端人學會看源碼。公眾號愿景:幫助5年內前端人走向前列。
識別上方二維碼加我微信、拉你進源碼共讀群
今日話題
略。分享、收藏、點贊、在看我的文章就是對我最大的支持~