🥵

フロントエンドのスピードに置いていかれたので、よく聞く技術を調べて分類してみた

2024/04/28に公開
2

元フルスタックエンジニア(死語)をやらせていただいていたものです。
JavaScript(TS)周りの進歩が凄く、あまりにもついていけていなかったので、気になったワードを片っ端から整理してみました。

それぞれに対する説明の正しくないものが含まれてしまっている可能性があります。
そんなところを見つけたときは優しく教えてくださると助かります。

各ツールの詳細というよりは、それぞれがどんな役割のものなのかを記載しています。
この記事が誰かの助けになれば幸いです。

調査・分類した言葉(技術)たち

  • Hono
  • Bun
  • Deno
  • Biome
  • Vite
  • Webpack
  • Turbopack
  • esbuild
  • Babel
  • SWC
  • Prisma

まず上記に上げたものが、どういった機能を持つものなのかもわかりませんでした。
それを整理すると以下になるようです。

  • JavaScript Runtime
    • Deno
    • Bun
    • Node
  • トランスパイラ
    • Babel
    • SWC
    • (esbuild)
  • Module bundler
    • esbuild
    • Webpack
    • Turbopack
    • vite
  • Linter/Formatter
    • rome
    • Biome
  • Webフレームワーク
    • Hono
  • ORM
    • Prisma

JavaScript Runtimeとは?

端的に言うとJavaScriptの実行環境になります。
JavaScriptの場合、最もよく使われている実行環境且つイメージがしやすいのは「ブラウザ」とかでしょうか。

他にもサーバサイドjsと呼ばれるものでNode, Deno, Bun などがあります。

僕は実行環境というと「プログラムを解析して実行するところ?」と感じてしまったのですが、僕のイメージしていたものはEngineと呼ばれるものでした。

概念的にはRuntimeのほうが大きく、Runtimeの中にEngineを含んでいることが多いようです。
※ Engineについて後述します。

しかし実行環境と言われてもいまいちイメージが付きません。これを一言で表すと、どういうものになるのでしょうか。
結論、僕には一言で表せる語彙がなかったので雰囲気として以下のようなものと理解することにしました。

  • Runtimeは実行時の環境・構成・処理を管理するシステム群をパッケージングしたシステム
      • 実行されるプログラムのアクセスできる範囲を管理するセキュリティ機能
      • 実行されるプログラムが参照するライブラリを管理するパッケージ(Module)マネジメント機能
      • TSを実行可能にするためのトランスパイラ
      • ECMAScript以外のAPIを利用可能にするための実装

Runtimeの中にLinterやFormatterなど他にも多くの機能を含むものもありますが、それはRuntimeというものの特性というよりは開発を便利するために気が利いた追加機能たちなのかなと思いました。

あくまでこれはJavaScriptのRuntimeと言った場合で、Runtime自体は文脈によって指すものが変わることがあると思います。
単純にプログラムの実行時を指すこともあるので。Runtime Errorとか。

各Runtimeの違い

Runtime Engine Module
Node V8 CommonJS, ESM*1
Deno V8 ESM, CommonJS*1
Bun JavaScriptCore ESM, CommonJS

*1: 部分的に対応

他にも特徴や比較項目があるのですが、表にして比較しづらいのでテキストで記載します。

  • Node
    • デファクトスタンダードであることによって、比較時の基準になるもの
    • Productionで安定稼働させたい場合に、最も枯れているため有力
  • Deno
    • Nodeの失敗点を反省してNode作成者の一人が作り始めたもの
    • セキュリティに関して機能が強化されている
      • NodeはファイルシステムやネットワークにフルアクセスだがDenoは許可制
    • パッケージ管理が独特でimport時にパッケージやバージョンを指定
      • npmにあるものを利用することも可能
    • 基本的にESMのみ
    • TS, JSX, TSXサポート
    • Deno deployによるデプロイサポート
  • Bun
    • とにかく速い
      • ベンチマークに不正確な点がありという指摘がちらほら
      • とはいえ、遅いということはなさそうで正規表現などは他より速いらしい
    • ESM, CommonJSを混ぜて使える
    • npmに対応
    • TS, JSX, TSXサポート
    • Nodeとの完全互換を目指し中

これらを踏まえて、個人的に感じた使い分けは以下です。

Runtime 用途
Node 業務で一定規模以上のProduction環境への利用
Deno セキュリティの重要度が高いシステムの開発
Bun フロントエンド開発でエコシステムとして利用

基本的には安定を取ってNodeを選択しておけば間違いなさそうというのが正直なところです。

ただ個人開発や、小さなコマンドラインツール、runtimeというよりはテスト実行やパッケージマネージャの恩恵を受けるための開発環境として利用するという点でBun, Denoも良いのかなと思いました。
Denoに関してはセキュリティやDeploy周りが充実しているというメリットがあるので、それがNodeによって使えるライブラリやノウハウの充実度より上回る場合には選択肢になってきそうです。

これはあくまで安定を取ってるだけなので、何かあった時に自分でどうにかできる場合や解決したい問題がDeno, Bunで解決できそうな場合には業務でも十分選択できると思います。

特にBunはNodeへの互換性を重視しているため、Bunが安定したら載せ替えてしまうのも良さそうですね。
逆もしかりなので、Bunでとりあえず開発してBunで解決できない問題が出てからNodeに戻すのも選択肢としては有り得そうです。

ここまで言っておいて、個人的に一番興味があるのはDenoです。なんか「こうしたい」みたいな気持ちが伝わってくるので。(個人の意見です)

JavaScript Engineとは?

凄くざっくりと言ってしまうと、インタプリタやコンパイラにあたるものだと思います。
有名なものにV8があります。

これがJavaScriptを実際にパソコンが実行可能なものに変換して実行してくれています。

例えば今回調べた各Runtimeで採用されているEngineは以下のようになっていました。

Engine Runtime
V8 Chrome, Node, Deno
JavaScriptCore Safari, Bun

詳細は割愛します(書けるだけの知識もないんです)が、2024/04/27現在のV8 EngineはJITコンパイラとなっており大雑把には以下のように動作するようです。

  1. Engine内のインタプリタでバイトコードを生成
  2. バイトコードをインタプリタで逐次処理し機械語にして実行
  3. インタプリタでプログラムを実行してる間に情報を蓄積し、ある程度有効な情報が溜まったらコンパイラがより広い範囲で最適化しつつ機械語に変換
  4. コンパイルされた機械語を呼び出して実行
  5. 必要に応じてバイトコードに戻り、コンパイル
  6. 上記を繰り返す

個人的には、こっちのほうが実行環境という名前にふさわしいと思うのですが、世の中的にはそうではないんですかね。

トランスパイラとは?

トランスパイラはある言語で書かれたプログラムを他の言語変換してくれるツールです。
他にも新しい言語仕様で書かれたものを古い言語仕様のコードに変換してくれるものもトランスパイラと呼ぶようです。
同じ言語同士でも、別の形に変換するもののことを言うんでしょうね。

Babel, SWC, esbuild

Babelは元々新しいECMAScriptの仕様で書かれたものを古い仕様のものへ変換し、古いブラウザでも新しいブラウザでもシステムが動作するようにしてくれるツールだったようです。
そこにPluginという形でts->jsの変換が行える拡張を追加することでtsからjsへのトランスパイルにも対応可能になり、適用可能領域が広がっていったようです。

SWCはこのBabelをより高速に動作するものとして置き換えるような方向性で発展しているようです。
最近ではBabelではなくSWCを選ぶことが一般的になっているようで、今後は基本的にSWCが採用されていくものと思われます。

esbuildはModule Bundlerなのですがts->jsのトランスパイルも可能になっています。
ただ型チェックなどは行ってくれないようなので、トランスパイラとして使うというよりはビルドの中で必要に応じてトランスパイルの機能を適用するみたいな用途になるのでしょうかね。そもそもバンドラですもんね。

Module Bundlerとは?

凄く雑に言ってしまうと、プログラムを一つのファイルにまとめるものです。

昔JSはモジュール機構が存在せず、ブラウザで読み込むときも上から順番に読み込むため読み込み順に注意する必要があったり、名前空間の衝突が起きないように気をつけて実装をする必要がありましたよね。
そういうことを気にせずに、よしなにプログラムの依存を解決して一つのファイルにまとめてくれるのがModule Bundlerになります。
※ 正確にはサーバサイドJSが出てきてCommonJSが発足して〜、みたいな流れがあるはずです。

webpack, turbopack, vite, esbuild

このあたりは複雑過ぎて細かに説明する知識を持ち合わせていないので簡単に記載します。

webpackは長い年月をフロントエンドのModule Bundlerの代名詞として活躍してきたツールです。
バンドル機能だけでなくトランスパイルやCSSのプリプロセッサとしての機能も組み込むことができて、フロントエンド開発に必要な全てが行えそうな勢いのなんでもござれツールです。

Turbopackはwebpack, next, vercelの開発者がRustで開発を勧めているバンドラになります。
Next.jsはこのTurbopackがbuilt inされています。
まだ開発途中のようで、Next.js以外での利用方法などは不明です。今後のロードマップとしてSvelteへの統合などが考えられているようです。
現時点ではローカル開発でのみ利用可能で、next buildは出来ないようです。

viteは正確には公式でバンドルツールではないと自称していますが、バンドルできるツールです。
webpackを基準として記載するため、少しズルになりますがwebpackと比較して以下の特徴があります。

  • ホットリロードが速い
  • ビルドが速い
  • 起動が速い
  • 設定が簡単
  • 新しいため情報量は劣る

またviteは内部でesbuildを利用してバンドルを行っています。
viteは開発用ビルドと本番用で利用しているものが違うのですが、esbuildは開発用で利用されています。

esbuildはGo製のバンドルツールで、ネイティブで動作するためwebpackよりも高速です。

viteのほうが後発のため、なにか明確な理由がない限りはモジュールバンドルの機能はviteを利用するのが良さそうです。
個人的にwebpackは設定が難しくなりすぎており、職人が必要なレベルになり始めているので他のツールがあるなら喜んで使いたい気持ちです。
※ webpack自体は偉大な存在です

Next.jsでviteを利用する場合はサーバサイドの機能への対応に課題があるらしく、一部機能に制限がかかるようなので現実的にはNext.jsでの利用は避けたほうが良さそうです。

Linter/Formatter

Linter, Formatterについては説明不要だと思うのでしません。
ツールについては一言だけ。

Biome

Biomeはromeを開発していた人たちが新しく作成したツールです。
romeはメンテナンスが終了しており、現在はBiomeが開発されているという状況です。

これ以外にもFormatterにはprettier、LinterにはESLintなどがありますが、それらと少しカバーできる範囲に差はあるようです。
これらをどの程度カスタマイズして利用しているかによりますが、強いこだわりがない場合にはBiomeに乗っかってしまうのがシンプルなのかもしれません。

Hono, Prisma

Honoはweb frameworkでした。
PrismaはORMでした。

Honoは日本の方が開発をしており、cloudflare workers向けに作っていたもののようです。
ご本人も今はCloudflareにいらっしゃる様子?

HonoはUltrafastでとても軽量なフレームワークということで、非常に人気のようです。

まとめ

今回色々調べたことで今まであまり良くわかっていなかった言葉について理解を深めることが出来ました。
特に私はRuntimeとEngineに関しての理解が壊滅的だったため、そこが整理できたのは大きかったです。

そして元々「よくわかんねぇ...」とおもっていたModule Bundlerやビルド周りですが、やっぱり複雑だなと思いました。
viteが高速な仕組みやNativeESMがどんなものなのか、バンドルがどういう流れで行われているのかなども調査の中で触れたのですが、まだ掴みきれていません。
調べた先でよくわからないものがどんどん出てくるので沼ですね。

これらの周辺ツール的な技術以外にもNextやらRSCやらと追うべきものは盛り沢山です。
個人的にRSCはかなり興味が惹かれているので、そのへんもやっていきたいところです。

おまけにひとりごと

以下、調べながら悩んだり思考したログと推測です。推測なので事実かどうかはわかりません。

Q: バイトコード(中間言語)をJavaみたいにコンパイルして先に配置したらいいのに、なんでJSは実行時に変換をするんだろう?
A: jsは引数などに型定義がなく実行時まで型が確定しないので、事前に変換を行えない。仮に変換する場合すべての型に対するビルド結果を生成する必要があり実行ファイルが巨大になる。そのため事前に機械語にもバイトコードにも出来ない。PHP, Ruby, Pythonなどがコンパイルを事前にしないのも同様の理由。

Q: ReactのRuntimeにDeno, Bunどっちを選ぶのが良いんだろう?
A: ReactのRuntimeはブラウザしかないので選択の余地がないはず。Next.jsであればサーバサイドになんのRuntimeを選択するかの余地があるはず。ただRuntimeとしてというよりは、開発環境としてのBun, Denoと考えるのであれば選択できそう。その場合、個人的にはBunで良さそう。

Q: Denoはtsのimport構文を独自の形にしているけど、それはありなのだろうか? 破壊的すぎはしないだろうか?
A: ESMにはモジュールの読み込み方法や依存関係の解決方法の仕様は作成されておらず、それはRuntimeに任されている。ブラウザでもscriptタグのtype=moduleなどがあり、それと同じで別に破壊的なわけではない。

参考文献

色々勉強するために以下参照させていただきました。
みんな凄いね。こういうエンジニアになりたいね。

Discussion

raruraru

個人的に頻繁に聞くものを列挙したもので、網羅性が担保された記事ではないので、僕があんまり聞く機会がないVanJSは今のところ加筆予定がありません。
申し訳ございません。

これはVanJSが有名でないという意味ではなく、僕の偏った観測範囲ではあんまり聞く機会がないというだけですのでご了承ください