🌪️

You don't need Node.js

2024/04/02に公開
4

Node.jsはいらない場合がある、むしろいらない場合の方が多いかもしれない、
そしてDenoとBunを使い分けて代替する方法を説明するという記事です。

Post Node.js ランタイムの登場

Node.js のあとにできたランタイムがいくつも登場しています。

この中でも、人気であるDenoBunを中心に考えていきます。

DenoやBunに変えるメリット

これがなければNode.jsから変える必要はないと思います。

私は、以下の3つが、2ランタイムに共通して言える大きなメリットだと思います:

  • ネイティブTypeScriptサポート
  • 高速
  • Web標準

ネイティブTypeScriptサポート

現在、JavaScriptを記述するときは、TypeScriptを利用することが多いと思います。

Node.jsでTypeScriptを使うとき。tscでコンパイルしてから実行したり、ts-nodetsxで行っているのではないでしょうか?
これは不便だと思います。私はNode.jsよりDenoを先に使い始めていたので、Node.jsでTypeScriptを使ったとき、超不便だと思いました。

これがDenoやBunだと要らないのです。

deno run main.ts
bun main.ts

で動いちゃいます。
これは超便利です。

高速

DenoやBunは実行速度が速いです。

Bunのインストール速度

Bunの依存関係インストール速度は爆速です。npm iの25倍早くなるようです[1]

実行速度

次のファイル書き込みコードをNode/Deno/Bunそれぞれのランタイムで試してみます

import { writeFile } from 'node:fs/promises'

await writeFile('./a.txt', Math.random().toString())

30回平均が、以下のようになりました:

IMG_2855

速いですね。
Bunのトップページには、Node/Deno/Bunのベンチマークが載っています。

Webサーバーの応答速度などの項目があり、NodeよりDeno/Bunが高速です。

Web標準

DenoやBunは、Node.jsよりもWeb標準に忠実です。

例えばfetch。JavaScriptでデータを取得するのに現在ではスタンダードですよね?
これ、Node.jsではExperimentalなAPIなのです。少し前までは、node-fetchなどのPolyfillを使う必要がありました。
しかし、DenoやBunでは普通に使えちゃいます。

そのほかにも

  • ⭕️ 使える
  • 🔼 unstable/experimental
  • 🙅‍♀️ 使えない

として、こんな感じです(Node.js 20, Deno 1.42, Bun 1.1):

API Node.js[2] Deno Bun[3]
Web Crypto API (crypto) 🔼 ⭕️ ⭕️
Request/Response 🔼 ⭕️ ⭕️
FormData 🔼 ⭕️ ⭕️
WebSocket 🔼 ⭕️ ⭕️
ReadableStream/WriableStream 🔼 ⭕️ ⭕️
alert/prompt/confirm 🙅‍♀️ ⭕️ ⭕️
Web GPU 🙅‍♀️ 🔼 🙅‍♀️

すごいですね。DenoやBunはNode.jsよりWeb標準に忠実です。

しかし、 @oto さんからの指摘もありましたが、Deno/Bunは新興フレームワークのため、単純にDeno/Bunのstable/unstableを、Nodeのstable/unstableと同レベルで比較することは難しいと思います。

ESM

DenoおよびBun[4]は、デフォルトでESMを使用しています。
モジュールシステムとして、CommonJS ModulesよりESMを使うことが多い現在で、嬉しいですね。


Node.jsをやめたくなってきましたか?

DenoとBun

DenoとBunのメリットについて説明しました。次は、この2つの使い分けについてです。
DenoとBunを使い分けることで、Node.jsをやめることができます。

私なりの使い分け方は、1から作るならDenoNode.jsのプロジェクトを使うならBunです。
この2つの使い分けで、Node.jsを代替することができます。

Bunの方がNode.jsプロジェクトに適している理由

DenoとBunのnpmインストール速度

これは、Bunの方が速いです。実験してみました:

まず、DenoとBunのキャッシュを削除します。

rm -rf ~/.bun/install
rm -rf ~/.cache/deno

次に、Astroをインストールしてみます。

bun add astro
deno cache astro

の速度を比較するのです。

次のようなBunコードを使います。

await Bun.$`rm -rf node_modules`
// Bun
const bunStarted = performance.now()
await Bun.$`bun add astro`
const bunResult = performance.now() - bunStarted


await Bun.$`rm -rf node_modules`

// Deno
const denoStarted = performance.now()
await Bun.$`deno cache npm:astro`
const denoResult = performance.now() - denoStarted

console.log(`Bun: ${bunResult} ms`)
console.log(`Deno: ${denoResult} ms`)

こうなりました:
IMG_2853

npmインストール速度において、Bunは高速です。

Node.js の仕組みの問題

  • Denoは、基本的にpackage.jsonを使いませんが[5]、Bunでは使います。
  • Denoは、基本的にnode_modulesを使いませんが[6]、Bunでは使います。
  • Denoは、基本的に.tsなどの拡張子をつけてimportしますが[7]、Bunでは省略可能です。

Node.jsプロジェクト以外ならDeno

Denoは、Node.jsの互換性は意識はしていますが、Nodeとは別のところを行くランタイム、というイメージです。

ゼロコンフィグ

Denoは、設定ファイル不要です。
task.tsを作成すると、deno run task.tsで動きます。プロジェクトという概念なしで動かせます。

設定ファイルを作れないわけではなくて、ある程度の大きさのことをするときは作成するといいかもかもしれません。

手軽

Pythonは、気軽にプログラムを作成して実行できますが、Denoも同じ感じです。
シンプルに、CLIから計算処理、バックエンドも作れたりするランタイムです。

モジュールはグローバル

Denoのモジュールはグローバルにキャッシュされます。
また、DenoはURLでimportします。

import * as module from 'https://example.com/mod.ts'

のようなイメージです。これはグローバルにキャッシュされます。

また、npmモジュールを利用したいときは、npm:packageというURLを使えます。これもグローバルにキャッシュされます。

Pythonに似ています。


このように、DenoはもともとNode.jsの反省を踏まえて作られた、新しいランタイムを目指していたのに対し、BunはNode.jsとの互換性を意識して作られています。

そのため、Node.js用に作られたViteやNext.jsのようなフレームワークを使うときは、高速な代替であるBunを使うことがおすすめです。

一方、バックエンドやコンピュータ中心などのものを作ったりするときは、Denoがおすすめです。

Node.jsプロジェクトをBunにスイッチする

これは超簡単です。
Node.jsプロジェクト内で、npm iの代わりにbun iをして依存関係をインストールするだけです。
そして、node main.jsbun --bun main.jsnpm run devbun --bun run devにするだけです。

既存のプロジェクトでこっそりBunを使う

bun iは、package-lock.jsonおよびyarn.lockから自動で読み取ってインストールしてくれます。

bun i --no-save

をすれば、npmやyarnを使っているプロジェクトのうんざりする依存関係インストール時間を短縮できます。

Node.jsをやめられないところ

例えば、BunでViteを動かすとき、デフォルトでは、Node.jsが裏で動いたりしています。
bunx --bun viteのように--bunを使い、裏もBunで動かすこともできますが、SSRでは不安定だったりします。

また、DenoやBunのライブラリをnpmにpublishしたいときは、やっぱりnpmコマンドが必要なので、Node.jsは必要です。

さらに、DenoやBunはまだまだNodeと比べたら新しいもので、安定性に欠ける可能性があります。

これらの欠点を理解して、スイッチするかどうかを決めるといいかもしれません。

まとめ

もちろん一概に言えることではないですが、Node.jsのようにプロジェクトを作ったり、Node.jsのフレームワークを使うならBunに変えられます。単純にNode.jsをバックエンドのJavaScriptランタイムとして使うならDenoに変えることができます。

そして、変えることでそれぞれのランタイムのメリットを享受できます。
みなさんも、Node.jsを、代替してみてはどうでしょうか?

脚注
  1. https://bun.sh/docs/cli/install ↩︎

  2. https://nodejs.org/docs/latest-v20.x/api/ ↩︎

  3. https://bun.sh/docs/runtime/web-apis ↩︎

  4. https://bun.sh/docs/runtime/modules#module-systems ↩︎

  5. 使うことも可能 ↩︎

  6. 生成することも可能 ↩︎

  7. unstableなフラグで省略可能 ↩︎

GitHubで編集を提案

Discussion

ぶんちんぶんちん

やっぱ会社だと使いにくいからなー
ドキュメントも実装も完全ではないし。

たぬきの教祖たぬきの教祖

Bun.jsはようやく1.1が出て使い始めたけれど、
まずもってまだ不安定と言わざるをえない。
後は、Denoもそのままだとデプロイ先に困る。結局デプロイ後の環境に合わせた方が無難なケースは割とある。
少しずつ置き換わっていくのだろうが、Node.jsが今いらないか、と言われたら全く否

Otogawa Katsutoshi(oto)Otogawa Katsutoshi(oto)

客観的にまとめれていて読みやすい文章で素晴らしいと思います。

ただ素人質問で申し訳ないのですが、Web標準の各jsのランタイム毎のstable, unstalbeについて、
nodejsのstableがdenoやbunと同格の安定性を持っていると仮定するのはちょっとおかしい気がします。

linuxでいうとarchlinuxのstableがmanjaroのunstableだったりと、会社、組織によって基準が違います。
また、ユーザー数が全然違うのでバグ報告もnodejsのほうが多いはずです。
bunやdenoの安定性の基準がnodejsより客観的に厳しいという基準があるなら別ですが、
基本的に新興勢力のほうが自己申告の安定性を低く見積もるほうが妥当じゃないかと思います。
bunやdenoのほうが安定性について厳しいという何らかの基準がありますなら、教えていただけると幸いです。

nakasyounakasyou

コメントありがとうございます。

また、ユーザー数が全然違うのでバグ報告もnodejsのほうが多いはずです。

bunやdenoの安定性の基準がnodejsより客観的に厳しいという基準があるなら別ですが、
基本的に新興勢力のほうが自己申告の安定性を低く見積もるほうが妥当じゃないかと思います。基本的に新興勢力のほうが自己申告の安定性を低く見積もるほうが妥当じゃないかと思います。

確かに、安定性の問題は、古くからあるものの方が有利です。unstable/stableを一律に比較できないというのもおっしゃる通りあると思います。

bunやdenoのほうが安定性について厳しいという何らかの基準がありますなら、教えていただけると幸いです。

基準はないですが、Bunを使って、不安定だと感じたことは何度かありました。


そのため、安定性の問題をこの記事に書き足そうと思います。ありがとうございました。