この記事は、ニフティグループ Advent Calendar 2024 11日目の記事です。
こんにちは!
今回は、JavaScriptとTypeScriptの三大ランタイム環境であるDeno、Bun、Node.jsについて比較していこうと思います。
概要
- Node.js: 2009年に登場した最も成熟したJavaScriptランタイム
- Deno: 2018年にNode.jsの創始者によって開発された、より安全でモダンなランタイム
- Bun: 2022年に登場した高速で全機能を備えたJavaScript/TypeScriptランタイム
主な特徴
Node.js
- 広大なエコシステムと豊富なnpmパッケージ
- 長年の実績と安定性
- 非同期I/Oに最適化
Deno
- セキュリティ重視(デフォルトで安全)
- TypeScriptのネイティブサポート
- URLベースのモジュールインポート
Bun
- 高速な起動とランタイム実行
- Node.jsとの高い互換性
- 内蔵のパッケージマネージャとバンドラー
比較
- パフォーマンス比較簡単なベンチマークとして、各ランタイムで1から1000万までの数を数える処理の実行時間を比較してみましょう。
Node.js
- Homebrewや公式サイト(https://nodejs.org/en)などでインストールする
- benchmark.jsというテストファイルを作成する。
|
console.log('Starting benchmark...'); const start = process.hrtime.bigint(); let count = 0; for (let i = 0; i < 10000000; i++) { count++; } const end = process.hrtime.bigint(); const duration = Number(end - start) / 1_000_000; // ナノ秒からミリ秒に変換 console.log(`Counting completed in ${duration.toFixed(3)} ms`); console.log(`Final count: ${count}`); |
- 実行する
- 実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
1回目 $ node benchmark.js Starting benchmark... Counting completed in 7.381 ms Final count: 10000000 2回目 $ node benchmark.js Starting benchmark... Counting completed in 8.529 ms Final count: 10000000 3回目 $ node benchmark.js Starting benchmark... Counting completed in 8.092 ms Final count: 10000000 |
Deno
- Homebrewなどでインストールする
- benchmark.jsというテストファイルを作成する。
|
console.log('Starting benchmark...'); const start = performance.now(); let count = 0; for (let i = 0; i < 10000000; i++) { count++; } const end = performance.now(); const duration = end - start; console.log(`Counting completed in ${duration.toFixed(3)} ms`); console.log(`Final count: ${count}`); |
- 実行する
- 実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
1回目 $ deno run benchmark.js Starting benchmark... Counting completed in 10.852 ms Final count: 10000000 2回目 $ deno run benchmark.js Starting benchmark... Counting completed in 10.905 ms Final count: 10000000 3回目 $ deno run benchmark.js Starting benchmark... Counting completed in 9.019 ms Final count: 10000000 |
Bun
- Homebrewなどでインストールする
- benchmark.jsというテストファイルを作成する。
|
console.log('Starting benchmark...'); const start = process.hrtime.bigint(); let count = 0; for (let i = 0; i < 10000000; i++) { count++; } const end = process.hrtime.bigint(); const duration = Number(end - start) / 1_000_000; // ナノ秒からミリ秒に変換 console.log(`Counting completed in ${duration.toFixed(3)} ms`); console.log(`Final count: ${count}`); |
- 実行する
- 実行結果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
1回目 $ bun benchmark.js Starting benchmark... Counting completed in 7.849 ms Final count: 10000000 2回目 $ bun benchmark.js Starting benchmark... Counting completed in 9.821 ms Final count: 10000000 3回目 $ bun benchmark.js Starting benchmark... Counting completed in 14.227 ms Final count: 10000000 |
結果
すべてほぼ同等のパフォーマンスを示しています。
HTTPサーバー作成
今度は各ランタイムで簡単なHTTPサーバーを作成してみます。
Node.js
- server.jsというファイルを作成します。
|
const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello Worldn'); }); server.listen(3000, () => { console.log('Server running at http://localhost:3000/'); }); |
- 実行
|
$ node server.js Server running at http://localhost:3000/ |
- TypeScriptファイルを試してみる
- server.tsを作成する
|
import * as http from 'http'; const server: http.Server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello Worldn'); }); server.listen(3000, () => { console.log('Server running at http://localhost:3000/'); }); |
- 実行
- 実行するとエラー
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
$ node server.ts (node:38672) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. (Use `node --trace-warnings ...` to show where the warning was created) /Users/server.ts:1 import * as http from 'http'; ^^^^^^ SyntaxError: Cannot use import statement outside a module at wrapSafe (node:internal/modules/cjs/loader:1350:18) at Module._compile (node:internal/modules/cjs/loader:1379:20) at Module._extensions..js (node:internal/modules/cjs/loader:1518:10) at Module.load (node:internal/modules/cjs/loader:1249:32) at Module._load (node:internal/modules/cjs/loader:1065:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:158:12) at node:internal/main/run_main_module:30:49 Node.js v22.2.0 |
- TypeScriptをネイティブサポートしていないことが分かります。
Deno
Bun
- server.tsというファイルを作成します。
- BunはTypeScriptをネイティブサポートしています。
|
const server = Bun.serve({ port: 3000, fetch(req) { return new Response("Hello Worldn"); }, }); console.log(`Listening on http://localhost:${server.port}`); |
- 実行
|
$ bun run server.ts Listening on http://localhost:3000 |
DenoとBunの比較
1. モジュールのインポート:
- Deno: 外部モジュールをURLから直接インポートします。
|
import { serve } from "https://deno.land/std@0.181.0/http/server.ts"; |
- Bun: 組み込みのグローバル
Bun
オブジェクトを使用し、追加のインポートは不要です。
2. サーバーの設定:
- Deno:
serve
関数を使用し、ハンドラー関数と設定オブジェクトを渡します。
|
await serve(handler, { port: 3000 }); |
- Bun:
Bun.serve
メソッドを使用し、設定オブジェクトを渡します。
|
const server = Bun.serve({ ... }); |
3. リクエストハンドラー:
- Deno: 独立した関数として定義します。
|
const handler = (req: Request): Response => { ... }; |
- Bun: 設定オブジェクト内の
fetch
メソッドとして定義します。
比較ポイント
上記の比較結果では、各ランタイムの HTTP サーバー実装の違いがありました。
以下に、主要な比較ポイントをまとめました。
1. TypeScript サポート:
- Node.js: 追加のツール (ts-node) とタイプ定義 (@types/node) が必要。
- Deno: ネイティブサポート。
- Bun: ネイティブサポート。
2. セキュリティモデル:
- Node.js: デフォルトですべてのシステムリソースにアクセス可能。
- Deno: 明示的な権限付与が必要(
--allow-net
フラグ)。
- Bun: Node.js 互換のモデルを採用(デフォルトでアクセス可能)。
3. API設計:
- Node.js: 標準的な http モジュールを使用。
- Deno: モダンな Web 標準に基づいた API。
- Bun: シンプルなオブジェクトベースの API。
4. モジュールシステム:
- Node.js: ES Modules (import 構文) を使用。
- Deno: ES Modules と URL ベースのインポートを使用。
- Bun: 組み込みの Bun オブジェクトを使用。
5. 実行の簡単さ:
- Node.js: TypeScript を直接実行するには追加のセットアップが必要。
- Deno: 追加のセットアップなしで TypeScript を直接実行可能。
- Bun: 追加のセットアップなしで TypeScript を直接実行可能。
結論
結論として、Node.js、Deno、Bunの3つのランタイムは、それぞれ独自の強みを持ち、JavaScriptエコシステムの多様化と進化に大きく貢献しています。
また、これらのランタイムは急速に進化を続けているため、定期的に最新の動向をチェックし、必要に応じて選択を見直すことをお勧めします。
本ブログが、読者の皆様のプロジェクト選択や技術的な意思決定の一助となれば幸いです。
より詳細な情報や具体的な使用例については、各ランタイムの公式ドキュメントを参照することをお勧めします。
明日は、Tsan0409さんの記事です。お楽しみに。