Bun v0.7.1 改善了 Node.js 與 Vite 和框架的相容性,提升了 ES Modules 的載入速度,並修復了一些轉譯器的錯誤。
我們正在招募 C/C++ 和 Zig 工程師,一同打造 JavaScript 的未來! 加入我們的團隊 →
Bun 是一個速度極快的 JavaScript 執行環境、打包器、轉譯器和套件管理器 — 功能All-in-One。在過去幾個月中,我們發布了許多 Bun 的更新,這裡為您整理了重點回顧,以防您錯過。
v0.6.11
- 解決了v0.6.10
的發布版本建置問題。v0.6.12
-Error.stack
中的 Sourcemap 支援、Bun.file().exists()
和 Node.js 錯誤修復。v0.6.13
- 實作了 mockDate
、更快的 base64 編碼,以及WebSocket
和node:tls
的修復。v0.6.14
-process.memoryUsage()
、process.cpuUsage()
、process.on('beforeExit', cb)
、process.on('exit', cb)
和崩潰修復v0.7.0
- Web Workers、--smol、structuredClone()、WebSocket 可靠性改進、node:tls 修復等等。
安裝 Bun
curl -fsSL https://bun.dev.org.tw/install | bash
npm install -g bun
brew tap oven-sh/bun
brew install bun
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun
升級 Bun
bun upgrade
ES Modules 載入速度提升 30% - 250%
Bun 的轉譯器現在以平行方式運行,顯著提升了 ES Module 的載入效能。
❯ hyperfine "bun index.mjs" "~/.bun/bin/bun index.mjs"
Benchmark 1: bun index.mjs # New
Time (mean ± σ): 237.3 ms ± 8.9 ms [User: 603.5 ms, System: 129.1 ms]
Range (min … max): 220.3 ms … 253.4 ms 12 runs
Benchmark 2: ~/.bun/bin/bun index.mjs # Bun v0.7.0
Time (mean ± σ): 606.7 ms ± 52.9 ms [User: 554.1 ms, System: 79.7 ms]
Range (min … max): 565.4 ms … 744.1 ms 10 runs
Benchmark 3: node ./index.mjs # Node.js v20.4.0
Time (mean ± σ): 640.7 ms ± 42.2 ms [User: 744.6 ms, System: 212.2 ms]
Range (min … max): 619.3 ms … 759.0 ms 10 runs
Summary
'bun index.mjs' ran
2.56 ± 0.24 times faster than '~/.bun/bin/bun index.mjs'
2.70 ± 0.20 times faster than 'node ./index.mjs'
// Importing 10 copies of Three.js
console.log("Hello! #1");
export * as Three1 from "./node_modules/three1/src/Three.js";
export * as Three2 from "./node_modules/three2/src/Three.js";
export * as Three3 from "./node_modules/three3/src/Three.js";
export * as Three4 from "./node_modules/three4/src/Three.js";
export * as Three5 from "./node_modules/three5/src/Three.js";
export * as Three6 from "./node_modules/three6/src/Three.js";
export * as Three7 from "./node_modules/three7/src/Three.js";
export * as Three8 from "./node_modules/three8/src/Three.js";
export * as Three9 from "./node_modules/three9/src/Three.js";
export * as Three10 from "./node_modules/three10/src/Three.js";
console.log("Hello! #2");
並行轉譯器
我們透過讓 Bun 的轉譯器並行運行,提升了 ES Modules 的載入速度。此變更同時影響了 bun test 和 Bun 的執行環境。
Bun 會轉譯每個檔案。雖然 Bun 的轉譯器速度很快,但當匯入大量模組後,從磁碟讀取檔案、解析 JavaScript、TypeScript 或 JSX、列印輸出程式碼以及產生 sourcemap 的成本就會變得顯著。
此變更有助於 Bun 擴展到更大的專案。
目前,並行轉譯僅在以下有限的條件下進行:
- Import 陳述式
- 動態 import
CommonJS require
表達式目前還不是並行的。透過 import
匯入的 CommonJS 模組可能是並行的,但它們的依賴項都不是。我們未來可能會進一步調整此行為的細節。
並行性是基於模組的第一次匯入(基本上:「這種載入程式碼的方式是否允許 Promises?」)
此變更的一個有趣的副作用是,模組解析現在只會執行一次。先前,Bun 會針對每個 import 陳述式執行兩次模組解析。第一次是在解析時,第二次是在執行時。現在,模組解析只會在執行時執行。模組解析有快取,因此我不預期這會對效能產生顯著影響,但儘管如此,這仍然很有趣。
vite dev
在 Linux 上啟動速度提升 90%
一系列的修補程式讓 vite dev
在 Linux 上啟動速度提升了 90%!
macOS 的啟動效能也有所提升,但仍有許多工作尚待完成。
bun:sqlite 在回傳資料時速度提升 10%
我們針對 bun:sqlite 中的物件建立進行了最佳化,這使得我們的 sqlite 基準測試效能提升了 10%。
在下一個 Bun 版本中
— Jarred Sumner (@jarredsumner) 2023年7月24日
bun:sqlite 在回傳資料時速度提升 10% pic.twitter.com/QUUxvMO9mv
Workspace 套件現在可以在依賴項中指定 npm 版本
bun install
現在支援在 dependencies
中指定 npm 版本範圍以進行比對的 workspace 套件。
{
"name": "@tanstack/react-query",
"version": "4.32.0",
"dependencies": {
"@tanstack/query-core": "^4.32.0"
}
}
{
"name": "@tanstack/query-core",
"version": "4.32.0",
}
{
"name": "@tanstack/monorepo",
"version": "4.32.0",
"private": true,
"workspaces": [
"packages/*"
],
}
這讓程式庫作者可以針對本機開發和發布到 npm 使用相同的 package.json。當本機儲存庫中的 workspace 成員套件符合 npm 版本範圍時,Bun 將會使用本機 workspace 套件。否則,它將會使用遠端 npm 套件。
改進了對私有 registry 和 Verdaccio 的支援
先前,Bun 的 http 用戶端有時無法連線到在 Node.js 中可運作的主機名稱。這是因為未將與 Node.js 相同的選項傳遞給 getaddrinfo
,並且僅檢查 getaddrinfo
傳回的第一個位址,而不是全部。
這個錯誤最常表現為 Bun 無法連線到私有 npm registry,因為這些 registry 更常位於具有特殊 DNS 設定的私有網路中。
Verdaccio 是一個流行的私有 npm 代理 registry 伺服器,感謝修復此錯誤,您可以使用它作為 bun install
的 registry。
修復了 /absolute/path/to/bun.lockb
的錯誤
Bun 的 lockfile 是二進位格式。為了方便檢查,Bun 將其標記為可執行檔,您可以執行 ./bun.lockb
來取得列印出的 Yarn v1 風格 lockfile。
然而,之前存在一個錯誤,以這種方式傳遞絕對路徑會無法運作。現在已修復。
之前
/Users/jarred/Code/bun/bun.lockb
lockfile not found:
之後
/Users/jarred/Code/bun/bun.lockb
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
# bun ./bun.lockb --hash: BB257457B48409D5-2e33f90ba679b81f-4C68A5605FDDA532-9220e96d0851dbdf
...
Node.js 相容性改進
我們正努力使 Bun 更相容於 Vite 驅動的框架。
fs.watch 可靠性改進
@cirospaciari 發布了幾個修補程式,提升了 fs.watch
的可靠性和效能。
- 先前,
vite dev
會啟動 11 個執行緒來監看檔案。現在,只需要 1 個執行緒。 - 修復了發生權限錯誤時的崩潰問題。
- 在 macOS 上,指向目錄的符號連結並非總是會被監看。現在已修復。
- 在 macOS 上,當持續監看目錄時,watcher 有時會 panic。這已修復。
fs.promises.watch
不再導致程序永遠保持運行
path.format 錯誤修復
path.format
中的一個錯誤導致 vite build
為 css 檔案輸出 undefined
。
@paperclover 在 #3734 中修復了這個問題
path.join latin1 編碼問題
當將非 ascii latin1 字串傳遞給 path.join
時,其中的一個錯誤可能會傳回編碼不正確的路徑。
path.dirname 為根路徑傳回 '.'
path.dirname
為根路徑傳回 '.'
,這導致某些套件無法與 Bun 搭配使用。
感謝 @Hanaasagi 修復了這個問題。
StringDecoder toString latin1 錯誤修復
以下程式碼片段在 Bun 和 Node.js 中產生不同的輸出
const { StringDecoder } = require("string_decoder");
const assert = require("assert");
const buf = Buffer.from("dd", "hex");
const sd = new StringDecoder("latin1");
const s = sd.write(buf);
console.log(s);
assert.strictEqual(s, "Ý");
這是錯誤的,並且已修復,感謝 @Hanaasagi。
fs.readdir() 在傳回大量檔案時的崩潰錯誤修復
先前,當字串數量約為 1000 個左右時,fs.readdir()
會崩潰。此錯誤是因在原生程式碼中不正確地建立 Array
所導致,這種方式會導致垃圾回收在陣列傳回給 JavaScript 之前釋放該陣列。
async fs.readdir()、fs.stat、fs.lstat、fs.readFile
以下 node:fs
函數現在將使用 Bun 的執行緒池來執行其工作
Promises
fs.promises.readdir()
fs.promises.stat()
fs.promises.lstat()
fs.promises.readFile()
Callbacks
fs.readdir()
fs.stat()
fs.lstat()
fs.readFile()
先前,Bun 會讓它們傳回預期的值,但實際上並未在單獨的執行緒上執行工作。這有助於稍微縮短 vite dev
的啟動時間。
child_process.fork() 已部分實作
child_process.fork()
已部分實作,感謝 @sirenkovladd。IPC 支援尚未實作,將在未來的版本中新增。
stat() 處理大型檔案
先前,fs.stat()
處理大型檔案時會截斷額外的帶號整數字節,傳回不正確的值。這已修復。
import { statSync } from "fs";
// Before:
statSync("./8gb-file.txt").size; // -635437056
// After:
statSync("./8gb-file.txt").size; // 8589934592
fs.createReadStream() 中遺失最後一個位元組的錯誤修復
一個差一錯誤導致 fs.createReadStream()
中出現一些不正確的行為。這已修復,感謝 @Hanaasagi。
這最常在使用 Express 的 compression
npm 套件時出現。
Web Platform API 相容性改進
先前,Bun 在 fetch()
和 HTTP 用戶端中未使用 WebKit 的 URL 解析器來解析和正規化 URL。這導致了一些不相容性和錯誤。Bun 現在在 fetch()
和 HTTP 用戶端中使用 WebKit 的 URL 解析器。這也適用於 bun install
。
MessagePort
和 MessageChannel
現在已支援
MessagePort 和 MessageChannel API 是 Bun 中新的全域變數。感謝 @dylan-conway 使其正常運作,並感謝 WebKit/Safari 團隊的實作。
const { port1, port2 } = new MessageChannel();
port1.onmessage = (e) => {
console.log(e.data); // "hello!"
};
port2.postMessage("hello!");
Bun 之所以選擇 JavaScriptCore 而非採用伺服器端 V8 單一文化的原因之一,是因為 JavaScriptCore 和 WebKit/Safari 緊密結合。這表示 Bun 通常可以直接使用 WebKit/Safari 的 Web API 實作,而無需重新實作。這就是一個很好的例子。
fetch() 現在支援 file: URL
您現在可以使用 fetch()
從本機檔案系統載入檔案。
const res = await fetch(new URL("hello!.jpg", import.meta.url));
await res.arrayBuffer();
在內部,這大致等同於
import { join } from "path";
const arrayBuffer = await Bun.file(
join(import.meta.dir, "hello!.jpg"),
).arrayBuffer();
Web Crypto API 現在會保持事件迴圈運作
先前,以下程式碼永遠不會記錄「finished」
crypto.subtle.digest("SHA-256", new Uint8Array([1, 2, 3])).then(() => {
console.log("finished");
});
由於從未等待 crypto.subtle.digest
傳回的 promise,Bun 的事件迴圈會在 Web Crypto API 完成其工作之前退出。此錯誤已修復。現在,上述程式碼將如預期般記錄「finished」。
WebAssembly 和 Atomics 錯誤修復
Bun 的事件迴圈現在將等待 WebAssembly 完成載入後再退出程序。
先前,以下程式碼片段永遠不會記錄
import Parser from "web-tree-sitter";
const main = async () => {
console.log("start");
await Parser.init();
console.log("finished");
};
main();
您必須 await main()
。無論您是否偏好以這種方式運作,現狀都是一個錯誤,因為其他執行環境並非如此運作。這導致各種套件無法運作。例如,此錯誤影響了 SolidStart。
Atomics.wait() 也存在類似的錯誤。
我們對 JavaScriptCore 進行了小幅修改,以將任務推送到我們的事件迴圈。在源自 JavaScriptCore 的非同步工作中,僅影響 WebAssembly 和 Atomics。我們不會等待 FinalizationRegistry 執行其回呼,然後再退出程序(因為 FinalizationRegistry 本身就不保證會被呼叫)。
Bun.isMainThread
和 Promise.withResolvers()
您現在可以使用 Bun.isMainThread
來檢查您是否在主執行緒中。
import { isMainThread } from "bun";
console.log("isMainThread", isMainThread);
if (isMainThread) {
const worker = new Worker(import.meta.url);
const { promise, resolve } = Promise.withResolvers();
worker.addEventListener("open", () => {
resolve();
});
await promise;
}
您也可以使用 Promise.withResolvers()
來建立 promise 及其解析器。這是 TC39 stage3 提案。
expect(arg).pass() 和 expect(arg).fail()
jest-extended matcher expect(arg).pass()
和 expect(arg).fail()
現在已在 bun:test
中支援,感謝 @TiranexDev。
import { expect, test } from "bun";
test("expect().pass()", () => {
expect(1).pass("I have passed!");
});