Bun
在當今的技術世界中,JavaScript 已經成為了一門不可或缺的程式語言。它不僅僅是網頁開發的基礎,也在 Server 端、Mobile App、甚至是 Desktop App,也都能看到 JavaScript 發揮著重要的作用。然而,隨著 JavaScript 生態系統的不斷發展和擴大,開發者面臨著越來越多的挑戰,例如程式碼的編譯、打包、測試和部署等,這些都需要不同的工具和環境來完成,使得開發流程變得複雜且難以管理。在這種背景下,一個名為 Bun 的新 JavaScript Runtime 橫空出世,而它的目的就是為現代的 JavaScript 生態系統提供一個更高效、整合的解決方案。
接下來,我們將深入探討 Bun,了解它如何解決開發者的實際問題,並通過一些例子來展示 Bun 的應用並且測試其效能。
Bun 解決了什麼?
Bun 的開發者強調,這個工具可以簡化 JavaScript 的開發過程,它使得許多 Node.js 工具,例如 node、npx、nodemon 和 dotenv 或 cross-env 等都變得不再必要。而且 Bun 可以運行多種檔案格式,例如常見的 .js
、.ts
、.jsx
和 .tsx
,這意味著它可以取代像 tsc 和 babel 這樣的轉譯器。在測試方面,Bun 可以跟 Jest 相容,支援 snapshot 測試、覆蓋率和 mocking。此外,Bun 還是一款性能優異的 JavaScript 打包工具,並提供與 esbuild 相容的 API。最後,它也是一款與 NPM 相容的套件管理器。
不僅僅是另一種 JavaScript Runtime
當提到 JavaScript 的 Runtime,實際上是在描述一個能夠執行 JavaScript 代碼的系統。大多數開發者多少都知道 Chrome 和 Node.js 背後的 V8 引擎,但 Bun 選擇了一條不同的路,它採用了 JavascriptCore 作為其動力。這是由 Apple 為 Safari 瀏覽器打造的, JavascriptCore 是一個極度注重性能的解決方案。
然而,僅憑 JavascriptCore 引擎是不夠的。為了創建一個完整的 JavaScript Runtime,Bun 在這方面選擇從零開始使用 Zig 實作 API。Zig 是一種低階程式語言,與 C 或 Rust 類似,專門為了建立高性能的應用程式而設計。
這種方式不僅提供了更好的性能和記憶體管理,而且在啟動和運作時都確保了驚人的速度。結合了這些特點,Bun 確實已經成為了 Node.js 的有力競爭者。
套件管理效能測試
Bun 主打的部分還有安裝速度遠超其他的 npm、yarn,還有目前最快的 pnpm,所以來實測看看是否真的這麼快。
Next.js
先用 pnpm 測試安裝 Next.js,所有選項都用預設值:
最後 pnpm 的安裝時間一共是 4.83 秒。
Bun
接著用 Bun 來測試安裝 Next.js,選項也一樣用預設值:
最後 Bun 的安裝時間一共是 0.23 秒。
Yarn
順便補上 yarn 的安裝時間,一共花了 30.91 秒。
透過實際的測試,Bun 在安裝速度上的優勢非常明顯,毫無疑問地展示了極高的效能。
Server 效能測試
這部分將展示 Bun 和 Node.js Server 的性能測試。我們會為 Bun 和 Node.js 分別建立一個簡單的 Server,並使用 wrk 工具進行壓力測試,以評估它們在不同情況下的性能表現。
Bun
import { serve } from 'bun'
import qrcode from 'qrcode'
// QR code
async function generateQRCode(req) {
try {
const body = await req.json() // 使用 req.json() 方法來解析請求的 JSON 資料
const text = body.text
const qrCodeUrl = await qrcode.toDataURL(text)
return new Response(JSON.stringify({ qrCodeUrl }), {
headers: { 'Content-Type': 'application/json' }
})
} catch (error) {
return new Response(error.message, { status: 500 })
}
}
// 回應 Hello World
function respondHelloWorld() {
return new Response('Hello World', {
headers: { 'Content-Type': 'text/plain' },
status: 200
})
}
// 處理 HTTP 請求
async function fetch(req) {
const url = new URL(req.url)
if (req.method === 'POST' && url.pathname === '/bun-generate-qrcode') {
return await generateQRCode(req)
}
if (req.method === 'GET' && url.pathname === '/bun-hello') {
return respondHelloWorld()
}
return new Response('Not Found', { status: 404 })
}
// 啟動服務器
serve({
fetch,
port: 3000
})
console.log('Server is running on port 3000')
Node.js
const http = require('http')
const QRCode = require('qrcode')
const { parse } = require('querystring')
// QR code
async function generateQRCode(req, res) {
let body = ''
req.on('data', (chunk) => {
body += chunk.toString()
})
req.on('end', async () => {
const text = JSON.parse(body).text
try {
const qrCodeUrl = await QRCode.toDataURL(text)
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ qrCodeUrl }))
} catch (error) {
res.writeHead(500, { 'Content-Type': 'text/plain' })
res.end(error.message)
}
})
}
// 回應 Hello World
function respondHelloWorld(req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' })
res.end('Hello World')
}
// 建立 HTTP Server
const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/node-generate-qrcode') {
generateQRCode(req, res)
} else if (req.method === 'GET' && req.url === '/node-hello') {
respondHelloWorld(req, res)
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' })
res.end('Not Found')
}
})
server.listen(3000, () => {
console.log('Server is running on port 3000')
})
在這兩個服務器實現中,我們分別提供了兩個路由,/hello
和 /generate-qrcode
。其中,/hello
路由將回傳 “Hello World” 文字,而 /generate-qrcode
則是會產生一個 QR Code 圖片。
為了更方便區分兩個 Server 的回應,我們將 Bun Server 的 /hello
路由改為 /bun-hello
,將 Node.js Server 的 /hello
路由改為 /node-hello
,以此類推,也將 /generate-qrcode
的部分做相同的修改。
接下來,我們使用 wrk 工具來進行壓力測試。
首先測試 GET:
指標 | Node.js | Bun |
---|---|---|
請求每秒 (RPS) | 56462.43 | 71974.89 |
平均延遲 | 4.37 毫秒 | 3.28 毫秒 |
最大延遲 | 150.14 毫秒 | 36.42 毫秒 |
吞吐量 | 9.58 MB/秒 | 7.76 MB/秒 |
- Bun 在每秒請求量(RPS)和平均延遲上優於 Node.js。
- Node.js 的吞吐量稍高,可能是由於 Node.js 有較好的資料傳輸效率。
- Node.js 的最大延遲遠高於 Bun,這可能是由於 Node.js 在處理極端情況時的表現不如 Bun。
接著測試 POST:
指標 | Node.js | Bun |
---|---|---|
請求每秒 (RPS) | 1051.85 | 810.91 |
平均延遲 | 225.04 毫秒 | 291.27 毫秒 |
最大延遲 | 364.67 毫秒 | 644.04 毫秒 |
吞吐量 | 1.20 MB/秒 | 0.88 MB/秒 |
- 在每秒請求量(RPS)上,Node.js 的表現優於 Bun。
- Bun 的平均延遲較高,這可能是因為 Bun 在處理 POST 請求時的效率較低。
- Node.js 的吞吐量也略高,這可能是因為 Node.js 在處理資料傳輸時的效率較高。
- Bun 的最大延遲較高,這可能是 Bun 在處理極端情況時的效率較低。
綜合分析 GET 和 POST 的測試結果,我們可以得出以下結論:
Bun 在 GET 請求的處理上展現出很強的性能優勢,尤其是在每秒請求量和延遲方面。這可能使得 Bun 更適合於讀取密集型的應用或服務。
反之,Node.js 在處理 POST 請求時表現較好,特別是在每秒請求量和吞吐量方面。這可能意味著 Node.js 在寫入密集型或資料傳輸密集型的應用中可能會有較好的表現。
結論
綜上所述,Bun 作為一個新興的 JavaScript Runtime,其快速的套件安裝速度、良好的性能和簡化的開發流程無疑為 JavaScript 生態系統帶來了新的可能。特別是在套件管理效能測試和服務器效能測試中,Bun 顯示出了相當的優勢。然而,每個工具都有其特定的適用場景和局限性,開發者在選擇使用 Bun 或 Node.js 時,應根據自身的項目需求和團隊經驗來做出判斷。隨著 Bun 的不斷發展和完善,我們有理由相信它會成為 JavaScript 生態系統中的重要一員。