試圖顛覆 JavaScript 生態(tài)?親身試用新 JS 運(yùn)行時(shí) Bun 后,我充滿期待(javascript新版本)
終于有人站出來,打算跟 JavaScript 生態(tài)系統(tǒng)正面交鋒了。這家伙知道自己在干什么,而且也描繪出了干掉 JS 之后要?jiǎng)?chuàng)造的美好新世界。
2022 年,前 Stripe 開發(fā)人員 Jared Sumner 發(fā)布了Bun,一種用 Zig 編程語言開發(fā)的運(yùn)行時(shí)。據(jù)我所知,Bun 最初只是種 JavaScript webserver,但在后續(xù)發(fā)展中逐漸醞釀出了全面顛覆 JS 生態(tài)系統(tǒng)的野心。
按我個(gè)人的關(guān)注度排序,Bun 的優(yōu)勢(shì)主要有以下幾點(diǎn):
- 據(jù)說能提供比 Node 或 Deno 更快的 JavaScript/TypeScript 運(yùn)行時(shí)
- 包管理器比 NPM 或 Yarn 都快上億倍
- Browser Bundler——全面支持 tsx、jsx、CSS、svg 等格式,能替代從 webpack 到 react-scripts 的所有內(nèi)容,而且速度仍然快如閃電
- 提供速度極快的 webserver(替代 Express)
- Sqlite 客戶端
- Bread
Bun 改朝換代的思路看著非常簡(jiǎn)單粗暴——JS 有的我也要有,而且我的要更簡(jiǎn)單、更高效。這里沒有小聰明、沒有曲線救國(guó),要的就是正面對(duì)抗而且樣樣比 JS 強(qiáng)。用一種低級(jí)語言,編寫出運(yùn)行極快的代碼,這就是 Bun。
Bun 還很年輕,也許還沒準(zhǔn)備好迎接那些令人頭大的真實(shí)生產(chǎn)用例。但它確實(shí)發(fā)展迅速,所以如果 Bun 真能在幾年后快速占據(jù)市場(chǎng)份額,我也覺得完全在情理之中。
之前的方案到底有什么問題?
不知道大家在實(shí)際工作中有沒有編寫過 JS 或 TS 生產(chǎn)代碼,那種體驗(yàn)挺難受的。多數(shù)情況下,開源工具和小項(xiàng)目也能良好運(yùn)轉(zhuǎn),但一到商業(yè)和企業(yè)級(jí)用例上就經(jīng)常掉鏈子。而因?yàn)閭鹘y(tǒng)、常規(guī)的路線走不通,企業(yè)只能試遍各種辦法讓項(xiàng)目能在生產(chǎn)環(huán)境中正常起效。
例如,TypeScript 在涉及多位開發(fā)者的項(xiàng)目中解決了不少老大難問題,所以只要 JS 的路子走不通,我們就能隨時(shí)引入 TS 進(jìn)行代碼轉(zhuǎn)換。這里要真心感謝微軟。NPM 對(duì)大型項(xiàng)目和單體 repo 來說速度太慢,所以公司可能需要轉(zhuǎn)向 Yarn。這里又要謝謝 Facebook。總之,我們就是在拼了命地東拼西湊,最終搞出性能勉強(qiáng)說得過去的成果。
作者提到自己所在企業(yè)的整個(gè)單體 repo 執(zhí)行 eslint 需要耗費(fèi) 79 秒,所以只能單獨(dú)配置,保證只對(duì)發(fā)生變更的文件執(zhí)行 lint。雖然會(huì)引入更多復(fù)雜元素,但也沒有辦法。
總的來說,無數(shù)開發(fā)者都在用自己的辦法加速 JS 工具鏈中的某些特定部分。比如用 Yarn 3 那瘋狂的“即插即用”節(jié)點(diǎn)模塊虛擬化速度來替代 NPM,或者用基于 JSON Schema 的請(qǐng)求解析器解決 Express 的低速問題。其實(shí)大多數(shù)原有工具都有類似的問題,而且它們是由 JS 開發(fā)者編寫、專為 JS 開發(fā)者服務(wù)的。用 JS 編寫,就等同于速度很慢……
于是,一些用更快語言編寫的高速工具開始流行起來。每家擁有大型 React 應(yīng)用程序的企業(yè),肯定都經(jīng)歷過 WebPack 構(gòu)建要花掉整整一分鐘的折磨。為此,他們必須轉(zhuǎn)向用 Go 語言編寫 esbuild。同樣的,其他語言版本的 eslint 替代方案也開始出現(xiàn),比如用 Rust 重寫 Rome。
Bun 是這種趨勢(shì)的自然延續(xù),但采取的卻是自下而上的推進(jìn)路徑。這個(gè)項(xiàng)目的核心思路就是從零起步、以內(nèi)置“batteries”的方式,用低級(jí)語言重寫整個(gè) JavaScript 生態(tài)系統(tǒng)。而且到目前為止,效果還真心不錯(cuò)。
Bun 現(xiàn)在可以做些什么?
讓解釋器快起來
如果 Bun 只是對(duì)所有 JS 輔助工具進(jìn)行重寫,我當(dāng)然也很歡迎,但那樣的它只能算是 Node.js 的又一個(gè)替代品。Bun 并沒有這樣偷懶,它努力讓解釋器本身也快起來。
Bun 是用 Zig 編寫的,而且配合蘋果開發(fā)的 JavaScriptCore,類似于 Node 使用 v8。Zig 是一種新興的低級(jí)語言,主要活躍在 C 占主導(dǎo)地位的場(chǎng)景。我不是低級(jí)開發(fā)者,所以沒親自用過,更多細(xì)節(jié)就留給其他技術(shù)更強(qiáng)的博主吧。在本文中,大家只要知道 Zig 寫的代碼很快就行了。至于 JavaScriptCore,它的作用跟 v8 一樣,只是 v8 來自谷歌、而它來自蘋果。Safari 和蘋果的很多其他項(xiàng)目都有用到 JavaScriptCore。
Bun 比 Node 到底快多少還沒有定論,但據(jù)稱在某些特定場(chǎng)景下要快得多。很多朋友可能沒經(jīng)歷過 io.js 剛誕生的時(shí)代,總結(jié)來說,那時(shí)候一個(gè)單純能提高解釋器速度的分叉就足以撼動(dòng)整個(gè) JS 生態(tài)系統(tǒng)。而 Bun 的啟動(dòng)速度又比 Node 快得多。我自己的親身實(shí)驗(yàn)是 7 毫秒左右,大概比 Node.js 快了 10 倍,所以特別適合無服務(wù)器環(huán)境和邊緣計(jì)算場(chǎng)景。
這一波顛覆依靠的不只是速度優(yōu)勢(shì),Bun 還添加了不少優(yōu)秀的標(biāo)準(zhǔn)庫函數(shù)。例如,Bun.write()就是用于編寫文件的新函數(shù),它會(huì)返回一個(gè)承諾,而且號(hào)稱可以通過更適合的系統(tǒng)調(diào)用進(jìn)一步加快速度。
說起 Node API,Bun 目前已經(jīng)能支持約 90%的現(xiàn)有 Node API。Node 規(guī)模很大,其中總有一些別說沒用過、可能大家聽都沒聽過的東西(比如 new AsyncLocalStorage() ),所以能支持 90%已經(jīng)很好了。誰會(huì)運(yùn)行 NPM 上的所有包呢?根本不需要,而且基本不影響我們的日常開發(fā)。
順便說一句,TypeScript 在 Bun 這邊可是相當(dāng)有排面,直接調(diào)用 bun my-ts-file.ts 就行。Deno 對(duì) TS 的支持也就這個(gè)水平了。使用 Bun 對(duì)新項(xiàng)目進(jìn)行模板化,或者把 bun-types 添加到 tsconfig 當(dāng)中,IDE 中的自動(dòng)補(bǔ)全功能就將適用于這些新函數(shù)!
Bun 項(xiàng)目最初目標(biāo)之一就是創(chuàng)建一種更快、更強(qiáng)大的 TypeScript 編譯器。這個(gè)目標(biāo)現(xiàn)在已經(jīng)實(shí)現(xiàn),同時(shí)被淹沒在其他眾多功能中。但目前,它仍然無法支持某些比較高級(jí)的 TypeScript 配置和功能,例如裝飾器、tsconfig 中將多個(gè)配置合并起來的擴(kuò)展功能等。
替代 NPM
下面來聊 Bun 最振奮人心的能力之一——替代 NPM。它真的很快,能讓人人都滿意那種快。
在 Linux 上,bun install 的包安裝速度可以達(dá)到 npm install 的 20 倍到 100 倍。在 macOS 上,也能達(dá)到 4 倍到 80 倍。
我敢肯定,沒 cache 快,有 cache 更快,總之就是快。
之前就已經(jīng)有很多方案在努力幫 NPM 提速了。比如大家熟悉的 Yarn Plug-n-Play,它的思路就是徹底放棄 node_modules 文件夾來加快包安裝速度。雖然有一定效果,但在實(shí)際使用中,提速并沒有那么顯著,而且還需要處理大量 polyfill 和 escape-hatches 操作。能用是能用,但我個(gè)人實(shí)在是不想再用、也不打算向大家推薦。
Pnpm 是另一種新興的 NPM 替代方案,在繼續(xù)使用 TypeScript 編寫的同時(shí)實(shí)現(xiàn)了一部分智能優(yōu)化。在 pnpm 中,node_modules 是通過符號(hào)鏈接從全局緩存中訪問的,每個(gè)包都能在自己的獨(dú)立時(shí)間內(nèi)完成安裝,無需等待其他包完成當(dāng)前操作。
Bun 的基本思路跟 NPM 一樣,但速度卻更快。它有自己的 lockfile 格式,而且其中的 node_modules 和 package.json 看起來沒什么變化。如果大家對(duì)文件系統(tǒng)調(diào)用比較熟悉,可以結(jié)合低級(jí)訪問和快速語言實(shí)現(xiàn)極快的安裝效果,而且無需任何花哨的技巧。
現(xiàn)在,Bun 還不提供工作空間支持,所以暫時(shí)沒法對(duì)接那些期待它來拯救的大型單體 repo(我們的項(xiàng)目也屬于這類)。但好在 Bun 正保持著迅猛的發(fā)展速度,幾周前剛公布的路線圖也提到了工作空間支持。
請(qǐng)注意,大家不用全面轉(zhuǎn)向 Bun 也能把它當(dāng)成包管理器、轉(zhuǎn)譯器或者解釋器。只需要選擇我們需要的部分,丟棄其余的部分就行。我猜 Bun 的初步普及可能也會(huì)走這條道路,就是先當(dāng)個(gè)好用的包管理器,其他的以后再說。這樣接受門檻會(huì)變得更低一些。
內(nèi)含轉(zhuǎn)譯器,矛頭指向 webpack、esbuild
Bun 當(dāng)中包含一個(gè)用于網(wǎng)絡(luò)瀏覽器的轉(zhuǎn)譯器,這明顯是把矛頭指向了 webpack 和 esbuild。順帶一提,Bun 中的解析器就是 esbuild 解析器的一個(gè) Zig 端口,輕松愉快。
Bun 已經(jīng)支持多種文件類型,css、svg、tsx、jsx、ts 之類的都行。JS 中的 CSS 等高級(jí)選項(xiàng)似乎也能在 Bun 上正常工作。
由于 Bun 包含一個(gè)帶有幾套內(nèi)置模板的項(xiàng)目腳手架,所以這里我們可以直接調(diào)用:
bun create react my-app
之后,我運(yùn)行 bun dev 并在瀏覽器里運(yùn)行了一個(gè) react 應(yīng)用程序。我猜可以把 react-scripts 直接添加到 Bun 替換過的工具列表當(dāng)中。
把文件擴(kuò)展名從 jsx 改成 tsx,程序就立刻生效了。導(dǎo)入 svg,沒有問題。開發(fā)模式似乎還支持 HMR,也就是前端開發(fā)者在使用 webpack 時(shí)的一大必備工具。
那么,轉(zhuǎn)譯器方面還缺什么嗎?缺的還多,畢竟生產(chǎn)環(huán)境的要求可不簡(jiǎn)單。首先就是最小化了,這是實(shí)際用戶最希望在后續(xù)發(fā)展路線圖上看到的功能。對(duì)于大型插件生態(tài)系統(tǒng)來說,還必須要有能夠支持不同文件格式的打包工具。例如,目前.vue 文件和.scss 還沒有實(shí)際落地,特別是.scss,這東西幾代開發(fā)者都在用,必須趕緊實(shí)現(xiàn)。目前我還不確定 Bun 捆綁器的可插拔性怎么樣,而且最重點(diǎn)的是要直接在框架之內(nèi)解決問題,不要依賴大量外部開源包。
其他功能——Web server 與 sqlite 客戶端
Bun 還把不少傳統(tǒng)意義上的框架元素添加到了標(biāo)準(zhǔn)庫當(dāng)中。就個(gè)人而言,我對(duì)那些庫類型功能不太感興趣,畢竟 Node 中已經(jīng)有很多適用于 http server 的功能長(zhǎng)城了。
Bun 的 webserver 看起來非常簡(jiǎn)單。Express 雖然有點(diǎn)落后于時(shí)代,但對(duì)大多數(shù)開發(fā)者來說仍然夠用(開發(fā)團(tuán)隊(duì)今年還剛剛提供了對(duì)承諾的支持)。Bun server 好像跟 Cloudflare Worker 頗為相信。只要 JavaScript 生態(tài)中的其他問題逐一得到解決,也許 Bun 的開發(fā)團(tuán)隊(duì)會(huì)轉(zhuǎn)回頭好好打磨一下 webserver 吧。需要注意的是,在某些情況下,巧用系統(tǒng)調(diào)用可以讓 Bun webserver 的速度提高一倍,特別是在文件處理過程中。
至于新的 SQLite 適配器,我覺得之前 Node 中的 sqlite 實(shí)現(xiàn)思路有點(diǎn)脫離正常人的腦洞。現(xiàn)在大多數(shù)開發(fā)者會(huì)把舊有 sqlite 3 包跟 sqlite 打包器結(jié)合使用,借此實(shí)現(xiàn)對(duì)承諾的支持。Bun 的解決方案看起來更簡(jiǎn)潔,所以就算速度上沒啥大優(yōu)勢(shì),我也愿意用。
酒香也怕巷子深
我最擔(dān)心的是,Bun 的這么多優(yōu)點(diǎn)難以轉(zhuǎn)化成對(duì)社區(qū)成員的實(shí)際吸引力。Bun 本身就是 JS 生態(tài)系統(tǒng)的完整替代品,這么巨大的轉(zhuǎn)變一般人恐怕很難快速接受。
Bun 還很年輕,目前沒有完整的說明文檔。對(duì)于大多數(shù)問題,我們只能查閱長(zhǎng)長(zhǎng)的自述文件。但創(chuàng)建一個(gè) docusaurus 站點(diǎn),再配合具備完整內(nèi)聯(lián)注釋的 TypeScript 類型生成相應(yīng)的 typedoc 并不困難,所以我猜這一點(diǎn)應(yīng)該很快就能解決。
其他產(chǎn)品對(duì)比
服務(wù)端渲染 React 每秒 HTTP 請(qǐng)求數(shù) (Linux AMD64) 對(duì)比,來自 Bun 官網(wǎng)
Deno
如果你從來沒聽說過 Deno、也不打算了解,直接跳過這章也行。而且就個(gè)人而言,我覺得 Bun 比 Deno 更有搞頭、更有前途。
來自 Node 締造者的 Deno 宣稱解決了一些長(zhǎng)期困擾開發(fā)者的老大難問題。它把 es-modules 設(shè)定成默認(rèn)值,引入了第一方 TypeScript 支持(無需在發(fā)布前轉(zhuǎn)譯 NPM 模塊)等等。但在我看來,Deno 在解決老問題的同時(shí),也引入了不少新問題。
首先,Deno 對(duì)包解析和語法做的變更過于大刀闊斧,導(dǎo)致沒法跟原有 NPM 生態(tài)系統(tǒng)兼容。換言之,Deno 需要培養(yǎng)起自己的全新庫生態(tài)。雖然 Deno 慢慢開始支持一些早期庫,但我覺得一個(gè)項(xiàng)目的影響力會(huì)直接決定它的發(fā)展上限,所以 Deno 的邊界估計(jì)也就到這了。當(dāng)然也有一些變通方法,比如把 NPM 包轉(zhuǎn)換成 Deno 包的 CDN,但我覺得這不是什么好招。
Deno 還有不少在我看來暴露其半成品身份的問題,比如缺少 package.json。無論是從模塊解析的角度來看,還是從缺少 manifest 文件出發(fā),Deno 都不允許開發(fā)者為自己的包編寫可擴(kuò)展元數(shù)據(jù)。GoLang 甚至專門為此引入了 go.mod。
另外,我覺得 Deno 設(shè)計(jì)中的沙箱/權(quán)限系統(tǒng)應(yīng)該是正確的思路,只是粒度不夠細(xì)。它位于整個(gè)項(xiàng)目的頂層、脫離了包層次,這意味著大型應(yīng)用程序最終還是需要所有權(quán)限,于是問題又回到了原點(diǎn)。而且作為一家安全公司,我們對(duì) Deno 無法保護(hù)大型應(yīng)用免受供應(yīng)鏈攻擊而頗感失望。當(dāng)然,Bun 也沒說打算如何解決這個(gè)問題,我這里只是發(fā)泄一下自己的不滿。
所以總結(jié)起來:Bun 擁有遠(yuǎn)超 Deno 的發(fā)展?jié)摿Α?span style="color: #1C1E21">具體原因如下:
- 它支持現(xiàn)有 NPM 生態(tài)系統(tǒng)中的所有庫,也支持大家已經(jīng)編寫的一切代碼,甚至連 package.json 都不用改。
- 它解決了生態(tài)系統(tǒng)中的幾個(gè)突出問題(特別是大企業(yè)的訴求問題),而且把解決方案整合到了單一框架當(dāng)中。
- 它以人們已經(jīng)熟悉的方式運(yùn)行,只是速度更快。不需要改變范式,也不強(qiáng)求轉(zhuǎn)變思路,用就是了。
- 可以放心使用,哪怕感覺 Bun 拖慢了開發(fā)速度,我們也可以只用它的包管理器;或者說覺得 webpack 太慢,那就只用它的打包程序。
- 它在幾乎各個(gè)維度上都更快,這是種巨大的優(yōu)勢(shì)。從 io.js 就能看到,人們是愿意為了性能而轉(zhuǎn)投陣營(yíng)的。
Rome?
如上所述,Rome 就是個(gè)驗(yàn)證器。Rome 的維護(hù)者們已經(jīng)開始用 Rust 代替 JS 進(jìn)行重寫了,而且 79 秒的驗(yàn)證時(shí)長(zhǎng)也有點(diǎn)夸張。(不騙人,我們的 eslint 就是用了 79 秒。)
從路線圖來看,Rome 還打算引入捆綁器、文檔生成器、壓縮器、類型檢查器、測(cè)試框架等等。但這一切尚未完成,而 Bun 明顯已經(jīng)走得更遠(yuǎn)。至少 Rome 還沒開始重寫 Node 核心本身,所以我覺得它的影響力也就差不多這樣了。
總之,很多項(xiàng)目都發(fā)現(xiàn)了 Node 生態(tài)系統(tǒng)中的現(xiàn)有問題,而且各自嘗試在統(tǒng)一的高性能框架中將其一舉解決。接下來,就看誰發(fā)展得更快了。
開源世界中的生態(tài)陣營(yíng)
這里我想把視野縮小一點(diǎn),通過具體案例聊聊開源世界中的生態(tài)陣營(yíng)是怎么產(chǎn)生的。
相信很多 Node 開發(fā)者都知道 Jest 是怎樣力壓 Mocha 測(cè)試框架,一路迅猛崛起的。Mocha 想當(dāng)年也是人們首選的測(cè)試運(yùn)行程序,效果不錯(cuò)而且語法優(yōu)秀,但只要涉及更復(fù)雜的需求或者斷言,就得引入其他模塊和插件。好在有了社區(qū)協(xié)作,插件也不算太難找??傊?,開發(fā)者需要具備更廣泛的知識(shí)才能引入相應(yīng)的庫。
后來 Facebook 搞出了 Jest,一套內(nèi)含“batteries”的測(cè)試框架。它借鑒了 Mocha 語法和庫,并把一切整合到了單一框架中。Jest 什么都能解決,從偽造時(shí)間到需求的檢測(cè)和模擬。Jest 也有擴(kuò)展空間,但我在實(shí)際工作中就用過一次。大部分概念驗(yàn)證和設(shè)計(jì)都是由 Mocha 承擔(dān)的,作為后來者的 Jest 只是把成果統(tǒng)一了起來并使其變得更易于訪問。雖然 Mocha 也不乏鐵桿粉絲,但 Jest 確實(shí)更受歡迎。
開源世界中有很多這樣的案例。首創(chuàng)解決方案拿下先發(fā)優(yōu)勢(shì),而后續(xù)一旦增長(zhǎng)乏力,就會(huì)有熱心的開發(fā)商把功能整合起來。這也讓我想到了 Linux 大家族還未統(tǒng)一時(shí)的 systemd。如今,systemd 幾乎可以管理大多數(shù) Linux 發(fā)行版上的所有內(nèi)容,而 Bun 也許會(huì)以同樣的方式席卷整個(gè) JavaScript 世界。
我意識(shí)到從開源的角度來看,這種合并和統(tǒng)一似乎與開源精神相悖,但用大量庫實(shí)現(xiàn)簡(jiǎn)單需求確實(shí)已經(jīng)成為折磨開發(fā)者們的痛點(diǎn)。而且如果每個(gè)庫都有相應(yīng)的維護(hù)團(tuán)隊(duì),那惡意黑客通過簡(jiǎn)單的偽造郵件域就能實(shí)施供應(yīng)鏈攻擊。我們不想這樣,但現(xiàn)實(shí)就是如此殘酷。老手尚且容易中招,更遑論剛接觸大量名稱、還不熟悉種種語言的新人了。所以從務(wù)實(shí)的角度出發(fā),我覺得很多朋友應(yīng)該跟我一樣,并不覺得把更多常用功能引入標(biāo)準(zhǔn)庫、將多種開發(fā)工具整合進(jìn)統(tǒng)一框架屬于歷史的倒退。
?結(jié)束語
截至 2022 年 7 月,Bun 還是沒有做好進(jìn)軍生產(chǎn)環(huán)境的準(zhǔn)備,但我強(qiáng)烈建議大家自己裝上試一試。整個(gè)流程非常便捷,而且我覺得現(xiàn)在的 Bun 已經(jīng)足夠應(yīng)付小型子項(xiàng)目或者公司里的簡(jiǎn)單內(nèi)部?jī)x表板了。
我不敢說 Bun 在未來幾年能否甚至如何重塑 JavaScript 的面貌,但我真心對(duì)它的發(fā)展充滿期待。
原文鏈接:
https://www.lunasec.io/docs/blog/bun-first-look/