我們正在招募 C/C++ 和 Zig 工程師,一同打造 JavaScript 的未來! 加入我們的團隊 →
我們最近為 Bun 發布了很多變更,這裡為您總結一下,以防您錯過任何資訊
v0.6.0
- 介紹bun build
,Bun 的全新 JavaScript 打包器。v0.6.2
- 效能提升:JSON.parse
速度提升 20%,Proxy
和arguments
速度提升高達 2 倍。v0.6.3
- 實作node:vm
,修復了node:http
和node:tls
的許多問題。v0.6.4
- 實作require.cache
、process.env.TZ
,以及bun test
速度提升 80%。v0.6.5
- 原生支援 CommonJS 模組 (先前,Bun 執行 CJS 到 ESM 的轉譯),v0.6.6
-bun test
改善,包括 Github Actions 支援、test.only()
、test.if()
、describe.skip()
,以及超過 15 個以上的expect()
匹配器;同時也包含使用fetch()
的串流檔案上傳。v0.6.7
- Node.js 相容性改進,以解除對 Discord.js、Prisma 和 Puppeteer 的封鎖
這個版本實作了頂層的 Bun.password
API,用於安全的密碼雜湊、函式模擬與 bun test
中的 toMatchObject
,以及 Bun.serve()
中實驗性的 inspector
模式,此外還有一些錯誤修正和穩定性改進。我們也對 bun:sqlite
進行了破壞性變更。
安裝 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
Bun.password
Bun 現在公開了 Bun.password
API,以便於以密碼學安全的方式雜湊密碼。
const password = "super-secure-pa$$word";
const hash = await Bun.password.hash(password);
// => $argon2id$v=19$m=65536,t=2,p=1$tFq+9AVr1bfPxQdh6E8DQRhEXg/M/SqYCNu6gVdRRNs$GzJ8PuBi+K+BVojzPfS5mjnC8OpLGtv8KJqF99eP6a4
雜湊演算法及其參數會編碼到傳回的雜湊值中,因此可以使用 Bun.password.verify
驗證雜湊值,而無需提供額外的元數據。
const password = "super-secure-pa$$word";
const hash = await Bun.password.hash(password);
const isMatch = await Bun.password.verify(password, hash);
// => true
預設演算法是 argon2
,但也支援 bcrypt
。兩種演算法都可以根據需要進行配置。
const password = "super-secure-pa$$word";
// use argon2 (default)
const argonHash = await Bun.password.hash(password, {
algorithm: "argon2id", // "argon2id" | "argon2i" | "argon2d"
memoryCost: 4, // memory usage in kibibytes
timeCost: 3, // the number of iterations
});
// use bcrypt
const bcryptHash = await Bun.password.hash(password, {
algorithm: "bcrypt",
cost: 4, // number between 4-31
});
當使用 bcrypt
時,傳回的雜湊值會以 模組化加密格式 編碼,以與大多數現有的 bcrypt
實作相容;而 argon2
的結果則以較新的 PHC 格式 編碼。在底層,Bun.password
使用了 Zig 標準函式庫中的 std.crypto.pwhash
模組。
請參閱 文件 > 雜湊 > Bun.password
以取得完整文件。
bun test
中的函式模擬
Bun 的測試執行器現在支援函式模擬以及一些相關的 expect()
匹配器。
import { test, expect, mock } from "bun:test";
const random = mock(() => Math.random());
test("random", async () => {
const val = random();
expect(val).toBeGreaterThan(0);
expect(random).toHaveBeenCalled();
expect(random).toHaveBeenCalledTimes(1);
});
mock()
的結果是一個新的函式,它被裝飾了一些額外的屬性和方法。
import { mock } from "bun:test";
const random = mock((multiplier: number) => multiplier * Math.random());
random(2);
random(10);
random.mock.calls;
// [[ 2 ], [ 3 ]]
random.mock.results;
// [
// { type: "return", value: 0.6533907460954099 },
// { type: "return", value: 0.6452713933037312 }
// ]
在 bun:test
中建立模擬的開銷很低且速度很快。在建立 100,000 個模擬的基準測試中,bun:test
比 Jest 快 80 倍,比 Vitest 快 100 倍。
建立 100,000 個模擬
— Jarred Sumner (@jarredsumner) 2023 年 6 月 9 日
bun:test:5 毫秒
jest:418 毫秒 (慢 80 倍)
vitest:786 毫秒 (慢 100 倍) pic.twitter.com/a3tjwrdT9u
jest.spyOn
支援
也新增了對 Jest 的 spyOn
功能的支援。這提供了一種追蹤和內省方法調用的機制。
import { test, expect, spyOn } from "bun:test";
const ringo = {
name: "Ringo",
sayHi() {
console.log(`Hello I'm ${this.name}`);
},
};
const hiMock = spyOn(ringo, "sayHi");
test("spyon", () => {
expect(hiMock).toHaveBeenCalledTimes(0);
ringo.sayHi();
expect(hiMock).toHaveBeenCalledTimes(1);
});
為了方便遷移,bun:test
導出了一個用於 jest
全域物件的 polyfill。這個 polyfill 實作了 jest.fn
和 jest.spyOn
,適用於已經依賴 Jest 風格語法的程式碼庫。
import { test, expect, jest } from "bun:test";
const mockFn = jest.fn(() => {});
const mockMethod = jest.spyOn(
{
random() {
return Math.random();
},
},
"random",
);
已新增對 .toHaveBeenCalled()
和 .toHaveBeenCalledTimes()
匹配器的支援。歡迎貢獻者提交 PR 以新增其他匹配器,例如 .toHaveBeenCalledWith()
和 .toHaveBeenLastCalledWith()
。
我們尚未新增對模組模擬的支援。請繼續關注未來的版本,但我非常確定我們可以使用相同的 API 使其適用於 CommonJS 和 ES Modules。
toMatchObject
toMatchObject
匹配器已新增至 bun:test
。此匹配器對於斷言物件是否包含屬性的子集很有用。
import { test, expect } from "bun:test";
test("toMatchObject", () => {
const obj = {
a: 1,
b: 2,
c: 3,
};
expect(obj).toMatchObject({
a: 1,
b: 2,
});
expect(obj).not.toMatchObject({
c: 4,
});
});
您也可以將 toMatchObject
與非對稱匹配器一起使用,例如 expect.stringContaining
和 expect.any(Number)
。
import { test, expect } from "bun:test";
test("toMatchObject", () => {
const obj = {
a: "hello",
b: "world",
};
expect(obj).toMatchObject({
a: expect.stringContaining("ell"),
b: expect.any(String),
});
});
(破壞性變更)bun:sqlite
的 .values()
方法現在傳回 []
而不是 null
之前,由於沒有匹配的列,以下程式碼會傳回 null
db.query("SELECT * FROM foo WHERE id > 9999").values();
// null
從 Bun v0.6.8 開始,它會傳回 []
db.query("SELECT * FROM foo WHERE id > 9999").values();
// []
這與其他函式和其他 SQLite3 函式庫的行為一致。
db.query("SELECT * FROM foo WHERE id > 9999").all(); // []
db.query("SELECT * FROM foo WHERE id > 9999").values(); // [] (previously: null)
我們進行了一項 Twitter 投票,75% 的人投票贊成這項破壞性變更。
在 `bun:sqlite` 查詢中調用 .values(),當結果為 0 時傳回 null 而不是 []
— Jarred Sumner (@jarredsumner) 2023 年 6 月 6 日
保持令人困惑的 API 不變還是進行破壞性變更?
Bun.serve
中的實驗性 inspector
模式
Bun 的 HTTP 伺服器現在提供「inspector」模式。要啟用它,請設定 inspector: true
。設定 NODE_ENV=production
會停用 inspector 模式,無論傳遞給 Bun.serve
的值為何。
Bun.serve({
inspector: true,
fetch(req) {
// handle request
},
});
這會在 localhost:$PORT/bun:inspect
掛載一個基於 WebSocket 的除錯器,它可以處理符合 WebKit 除錯器協定的訊息。這是朝向在使用 Bun 開發全端應用程式時,更進階的瀏覽器端除錯體驗邁出的第一步。
這個 gist 提供了一個簡單但功能完整的瀏覽器內「REPL」實作,它可以執行伺服器端程式碼並透過 WebSockets 檢索結果。
為了更輕鬆地開發針對 WebKit 除錯器協定的工具,我們已將 bun-devtools
發布到 npm
,其中提供了協定的操作、日誌等的自動生成的 Typescript 類型。
import { JSC } from "bun-devtools";
轉譯器與打包器錯誤修正
我們修正了打包器和轉譯器中的一些問題
lodash-es/isBuffer
打包失敗
由於 CommonJS 和 ES Module 互操作性的邊緣情況,lodash-es
導出的 isBuffer
無法運行。
以下程式碼來自 lodash-es/isBuffer.js
import root from "./_root.js";
import stubFalse from "./stubFalse.js";
var freeExports =
typeof exports == "object" && exports && !exports.nodeType && exports;
var freeModule =
freeExports &&
typeof module == "object" &&
module &&
!module.nodeType &&
module;
var moduleExports = freeModule && freeModule.exports === freeExports;
var Buffer = moduleExports ? root.Buffer : undefined;
var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;
var isBuffer = nativeIsBuffer || stubFalse;
export default isBuffer;
對 module
和 exports
的 typeof
檢查是不正確的,因為模組始終是 ES Module,因此 module
和 exports
始終未定義。CommonJS 識別符的使用導致 Bun 將其視為 CommonJS 模組,從而導致運行時異常。這個問題已修正。
然而,lodash-es
中的 isBuffer
函式在 Node.js 和 Bun 中仍然損壞(它始終傳回 false
)。我們已向 lodash-es
提交了一個 issue。
標籤模板字面量中的 \r\n
正規化
在某些情況下,Bun 會錯誤地正規化標籤模板字面量中的 Windows 換行符。這個問題已修正。
RegExp 中的 v
flag
當在 RegExp 字面量中使用 v
flag 時,Bun 會錯誤地拋出語法錯誤。v
flag 是 stage4 TC39 提案。
bun install
工作區生命週期腳本錯誤修正
bun install
現在為工作區套件運行生命週期腳本。以前,僅運行根套件的生命週期腳本。
感謝 @alexlamsl 的修正!
console.log(myBuffer)
顯示 Buffer 而不是 Uint8Array
之前,對於 Buffer
實例,console.log
會顯示 Uint8Array
而不是 Buffer
。這個問題已修正。
const buf = Buffer.from("hello world");
console.log(buf);
之前
Uint8Array(11) [ 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100 ]
現在
Buffer(11) [ 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100 ]
更新日誌
#3209 | [轉譯器] 修正標籤樣板字串字面值中 \r\n 的正規化,作者:@Jarred-Sumner |
#3208 | removeAllListeners 傳回 this ,作者:@dylan-conway |
#3204 | 實作 Bun.password 和 Bun.passwordSync ,作者:@Jarred-Sumner |
#3215 | [轉譯器] 修正原始模板內容的新長度,作者:@dylan-conway |
#3213 | 允許 regexp 字面值中使用 v flag,作者:@dylan-conway |
#3220 | console.log(Buffer.from('a')) -> Buffer(1) [... 而不是 Uint8Array(1) [... ,作者:@Jarred-Sumner |
#3219 | [破壞性變更][bun:sqlite] .values() 傳回 [] 而不是 null ,作者:@Jarred-Sumner |
#3254 | 在 GitHub Actions 中替換 sudo 用法,作者:@alexlamsl |
#3231 | 修正在移除套件後保留換行符號的問題,作者:@ytakhs |
#3252 | 在 bun:test 中實作模擬,作者:@Jarred-Sumner |