雑記帳

整理しない情報集

更新情報

最小のJSX/TSX構成を作ってみる

公開日:

カテゴリ: Javascript

React系のJSX/TSXを使ってみたかったのですが、普通にプロジェクトを作成すると大量の依存関係がインストールされてげんなりするので、なるべく依存関係の少ない代替の環境を探してみました。

なお、あくまで趣味レベルの小規模なプロジェクトを想定しているので、大規模なプロジェクトや本番環境対応などは特に考えていません。そのレベルの開発であれば、素直に公式で推奨されているフレームワークを使うのが良いと思います。

TL;DR

  • esbuildが便利
    • JS / TS / JSX / TSX のバンドル・トランスパイルが可能
    • ファイル監視による自動ビルド機能・簡易開発サーバ機能あり
    • npmからインストールせずに、バイナリ1個でも動作する

普通にReact+TSX環境を作ってみる

とりあえずReact公式からチュートリアルを探しに行きます。しかし、探せど探せどフレームワークへの案内しか見当たりません。

どうやら2025/02現在ではフレームワークなしのReactは標準から外れたようです。昔はcreate-react-appがあったような気がしますが、現在は公式ページには影も形もありません。フレームワーク必須のライブラリはどうなのかと思わなくもないですが、今回の趣旨とは関係ないのでひとまず置いておきます。

本項はフレームワークを使うことが目的ではないので、単純にReact+TSX環境を作る方法を探します。Deep Diveとして折りたたんだ先で紹介されているviteでインストールしてみます。

npm create vite@latest my-react-app -- --template react-ts
cd my-react-app
npm i

これ一つで開発環境の構築が完了し、開発に必要/開発体験が向上するツールがセットでインストールされるので、普通に開発する分にはこれで十分でしょう。

さて、依存関係を確認してみます。まずは直接インストールされたパッケージを調べます。

npm ls
[email protected] FILEPATH/my-react-app
+-- @eslint/[email protected]
+-- @types/[email protected]
+-- @types/[email protected]
+-- @vitejs/[email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
`-- [email protected]

次に依存関係の全リストを確認してみます。

npm ls --all

実行結果は長いので畳みます。

[email protected] FILEPATH/my-react-app
+-- @eslint/[email protected]
+-- @types/[email protected]
| `-- @types/[email protected] deduped
+-- @types/[email protected]
| +-- @types/[email protected]
| `-- [email protected]
+-- @vitejs/[email protected]
| +-- @babel/[email protected]
| | +-- @ampproject/[email protected]
| | | +-- @jridgewell/[email protected]
| | | | +-- @jridgewell/[email protected]
| | | | +-- @jridgewell/[email protected]
| | | | `-- @jridgewell/[email protected] deduped
| | | `-- @jridgewell/[email protected]
| | |   +-- @jridgewell/[email protected]
| | |   `-- @jridgewell/[email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | +-- [email protected] deduped
| | | `-- [email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | +-- @jridgewell/[email protected] deduped
| | | +-- @jridgewell/[email protected] deduped
| | | `-- [email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | +-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | `-- [email protected]
| | | |   +-- [email protected] deduped
| | | |   +-- [email protected]
| | | |   `-- [email protected] deduped
| | | +-- [email protected]
| | | | `-- [email protected]
| | | `-- [email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected]
| | | | +-- @babel/[email protected] deduped
| | | | `-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | `-- @babel/[email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected] deduped
| | | `-- @babel/[email protected] deduped
| | +-- @babel/[email protected]
| | | `-- @babel/[email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | `-- @babel/[email protected] deduped
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | +-- @babel/[email protected] deduped
| | | +-- [email protected] deduped
| | | `-- [email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | `-- @babel/[email protected] deduped
| | +-- [email protected]
| | +-- [email protected] deduped
| | +-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| +-- @babel/[email protected]
| | +-- @babel/[email protected] deduped
| | `-- @babel/[email protected]
| +-- @babel/[email protected]
| | +-- @babel/[email protected] deduped
| | `-- @babel/[email protected] deduped
| +-- @types/[email protected]
| | +-- @babel/[email protected] deduped
| | +-- @babel/[email protected] deduped
| | +-- @types/[email protected]
| | | `-- @babel/[email protected] deduped
| | +-- @types/[email protected]
| | | +-- @babel/[email protected] deduped
| | | `-- @babel/[email protected] deduped
| | `-- @types/[email protected]
| |   `-- @babel/[email protected] deduped
| +-- [email protected]
| `-- [email protected] deduped
+-- [email protected]
| `-- [email protected] deduped
+-- [email protected]
| `-- [email protected] deduped
+-- [email protected]
| +-- @eslint-community/[email protected]
| | +-- [email protected]
| | `-- [email protected] deduped
| +-- @eslint-community/[email protected]
| +-- @eslint/[email protected]
| | +-- @eslint/[email protected]
| | +-- [email protected] deduped
| | `-- [email protected] deduped
| +-- @eslint/[email protected]
| | `-- @types/[email protected] deduped
| +-- @eslint/[email protected]
| | +-- [email protected] deduped
| | +-- [email protected] deduped
| | +-- [email protected] deduped
| | +-- [email protected]
| | +-- [email protected] deduped
| | +-- [email protected]
| | | +-- [email protected]
| | | | `-- [email protected]
| | | `-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | +-- [email protected] deduped
| | `-- [email protected]
| +-- @eslint/[email protected] deduped
| +-- @eslint/[email protected]
| | +-- @eslint/[email protected] deduped
| | `-- [email protected]
| |   +-- [email protected] deduped
| |   `-- [email protected] deduped
| +-- @humanfs/[email protected]
| | +-- @humanfs/[email protected]
| | `-- @humanwhocodes/[email protected]
| +-- @humanwhocodes/[email protected]
| +-- @humanwhocodes/[email protected]
| +-- @types/[email protected]
| +-- @types/[email protected]
| +-- [email protected]
| | +-- [email protected] deduped
| | +-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| |   `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | |   `-- [email protected]
| | `-- [email protected]
| |   `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | `-- [email protected]
| |   `-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected] deduped
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected] deduped
| | +-- [email protected]
| | `-- [email protected] deduped
| +-- [email protected]
| | `-- [email protected] deduped
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| |   +-- [email protected]
| |   `-- [email protected]
| |     `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | |   `-- [email protected]
| | |     `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| | `-- [email protected] deduped
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| +-- UNMET OPTIONAL DEPENDENCY jiti@*
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| |   +-- [email protected]
| |   `-- [email protected]
| +-- [email protected]
| `-- [email protected]
|   +-- [email protected]
|   +-- [email protected]
|   +-- [email protected] deduped
|   +-- [email protected]
|   +-- [email protected]
|   | `-- [email protected] deduped
|   `-- [email protected]
+-- [email protected]
+-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| +-- [email protected] deduped
| `-- [email protected]
|   `-- [email protected] deduped
+-- [email protected]
| `-- [email protected] deduped
+-- [email protected]
| +-- @typescript-eslint/[email protected]
| | +-- @eslint-community/[email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected]
| | | +-- @typescript-eslint/[email protected] deduped
| | | `-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected]
| | | +-- @typescript-eslint/[email protected] deduped
| | | +-- @typescript-eslint/[email protected] deduped
| | | +-- [email protected] deduped
| | | +-- [email protected] deduped
| | | +-- [email protected] deduped
| | | `-- [email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected]
| | | +-- @typescript-eslint/[email protected] deduped
| | | `-- [email protected] deduped
| | +-- [email protected] deduped
| | +-- [email protected]
| | +-- [email protected] deduped
| | +-- [email protected] deduped
| | +-- [email protected]
| | | `-- [email protected] deduped
| | `-- [email protected] deduped
| +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | | +-- @typescript-eslint/[email protected] deduped
| | | +-- @typescript-eslint/[email protected] deduped
| | | +-- [email protected] deduped
| | | +-- [email protected]
| | | | +-- @nodelib/[email protected]
| | | | +-- @nodelib/[email protected]
| | | | | +-- @nodelib/[email protected]
| | | | | | +-- @nodelib/[email protected] deduped
| | | | | | `-- [email protected]
| | | | | |   `-- [email protected]
| | | | | `-- [email protected]
| | | | |   `-- [email protected]
| | | | +-- [email protected]
| | | | | `-- [email protected] deduped
| | | | +-- [email protected]
| | | | `-- [email protected]
| | | |   +-- [email protected]
| | | |   | `-- [email protected]
| | | |   |   `-- [email protected]
| | | |   |     `-- [email protected]
| | | |   `-- [email protected]
| | | +-- [email protected] deduped
| | | +-- [email protected]
| | | | `-- [email protected]
| | | |   `-- [email protected] deduped
| | | +-- [email protected]
| | | +-- [email protected] deduped
| | | `-- [email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- [email protected] deduped
| | +-- [email protected] deduped
| | `-- [email protected] deduped
| +-- @typescript-eslint/[email protected]
| | +-- @eslint-community/[email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- @typescript-eslint/[email protected] deduped
| | +-- [email protected] deduped
| | `-- [email protected] deduped
| +-- [email protected] deduped
| `-- [email protected] deduped
+-- [email protected]
`-- [email protected]
  +-- UNMET OPTIONAL DEPENDENCY @types/node@^18.0.0 || ^20.0.0 || >=22.0.0
  +-- [email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @esbuild/[email protected]
  | `-- @esbuild/[email protected]
  +-- UNMET OPTIONAL DEPENDENCY fsevents@~2.3.3
  +-- UNMET OPTIONAL DEPENDENCY jiti@>=1.21.0
  +-- UNMET OPTIONAL DEPENDENCY less@*
  +-- UNMET OPTIONAL DEPENDENCY lightningcss@^1.21.0
  +-- [email protected]
  | +-- [email protected]
  | +-- [email protected]
  | `-- [email protected]
  +-- [email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- UNMET OPTIONAL DEPENDENCY @rollup/[email protected]
  | +-- @rollup/[email protected]
  | +-- @types/[email protected] deduped
  | `-- UNMET OPTIONAL DEPENDENCY fsevents@~2.3.2
  +-- UNMET OPTIONAL DEPENDENCY sass-embedded@*
  +-- UNMET OPTIONAL DEPENDENCY sass@*
  +-- UNMET OPTIONAL DEPENDENCY stylus@*
  +-- UNMET OPTIONAL DEPENDENCY sugarss@*
  +-- UNMET OPTIONAL DEPENDENCY terser@^5.16.0
  +-- UNMET OPTIONAL DEPENDENCY tsx@^4.8.1
  `-- UNMET OPTIONAL DEPENDENCY yaml@^2.4.2

UNMET OPTIONAL DEPENDENCYと出力されている行は、他の環境向けのパッケージなので実際にはインストールされていません(今回はWindows環境での実行結果です)。dedupedと出力されている行は重複するため除外されたものです。

最終的にインストールされたものは以下のようになります。

npm ls --all | findstr /v "UNMET OPTIONAL DEPENDENCY" | findstr /v "deduped"
npm ls --all | grep -v -e "UNMET OPTIONAL DEPENDENCY" -e "deduped"
[email protected] FILEPATH/my-react-app
+-- @eslint/[email protected]
+-- @types/[email protected]
+-- @types/[email protected]
| +-- @types/[email protected]
| `-- [email protected]
+-- @vitejs/[email protected]
| +-- @babel/[email protected]
| | +-- @ampproject/[email protected]
| | | +-- @jridgewell/[email protected]
| | | | +-- @jridgewell/[email protected]
| | | | +-- @jridgewell/[email protected]
| | | `-- @jridgewell/[email protected]
| | |   +-- @jridgewell/[email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | +-- @babel/[email protected]
| | | `-- [email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | | +-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | `-- [email protected]
| | | |   +-- [email protected]
| | | +-- [email protected]
| | | | `-- [email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | +-- @babel/[email protected]
| | +-- @babel/[email protected]
| | +-- @babel/[email protected]
| | +-- @babel/[email protected]
| | | `-- [email protected]
| | +-- @babel/[email protected]
| | | +-- @babel/[email protected]
| | +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| +-- @babel/[email protected]
| | `-- @babel/[email protected]
| +-- @babel/[email protected]
| +-- @types/[email protected]
| | +-- @types/[email protected]
| | +-- @types/[email protected]
| | `-- @types/[email protected]
| +-- [email protected]
+-- [email protected]
+-- [email protected]
+-- [email protected]
| +-- @eslint-community/[email protected]
| | +-- [email protected]
| +-- @eslint-community/[email protected]
| +-- @eslint/[email protected]
| | +-- @eslint/[email protected]
| +-- @eslint/[email protected]
| +-- @eslint/[email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | +-- [email protected]
| | | | `-- [email protected]
| | | `-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | `-- [email protected]
| +-- @eslint/[email protected]
| | `-- [email protected]
| +-- @humanfs/[email protected]
| | +-- @humanfs/[email protected]
| | `-- @humanwhocodes/[email protected]
| +-- @humanwhocodes/[email protected]
| +-- @humanwhocodes/[email protected]
| +-- @types/[email protected]
| +-- @types/[email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| |   `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | |   `-- [email protected]
| | `-- [email protected]
| |   `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | `-- [email protected]
| |   `-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| |   +-- [email protected]
| |   `-- [email protected]
| |     `-- [email protected]
| +-- [email protected]
| | +-- [email protected]
| | | `-- [email protected]
| | |   `-- [email protected]
| | |     `-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| +-- [email protected]
| +-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| |   +-- [email protected]
| |   `-- [email protected]
| +-- [email protected]
| `-- [email protected]
|   +-- [email protected]
|   +-- [email protected]
|   +-- [email protected]
|   +-- [email protected]
|   `-- [email protected]
+-- [email protected]
+-- [email protected]
| +-- [email protected]
| | `-- [email protected]
| `-- [email protected]
+-- [email protected]
+-- [email protected]
| +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | +-- [email protected]
| | +-- [email protected]
| +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | +-- @typescript-eslint/[email protected]
| | | +-- [email protected]
| | | | +-- @nodelib/[email protected]
| | | | +-- @nodelib/[email protected]
| | | | | +-- @nodelib/[email protected]
| | | | | | `-- [email protected]
| | | | | |   `-- [email protected]
| | | | | `-- [email protected]
| | | | |   `-- [email protected]
| | | | +-- [email protected]
| | | | +-- [email protected]
| | | | `-- [email protected]
| | | |   +-- [email protected]
| | | |   | `-- [email protected]
| | | |   |   `-- [email protected]
| | | |   |     `-- [email protected]
| | | |   `-- [email protected]
| | | +-- [email protected]
| | | | `-- [email protected]
| | | +-- [email protected]
| +-- @typescript-eslint/[email protected]
+-- [email protected]
`-- [email protected]
  +-- [email protected]
  | `-- @esbuild/[email protected]
  +-- [email protected]
  | +-- [email protected]
  | +-- [email protected]
  | `-- [email protected]
  +-- [email protected]
  | +-- @rollup/[email protected]

次に、ファイル数やフォルダサイズを見ていきます。

ファイル数

dir node_modules /A-D /S /B | find /c /v ""
ls ./node_modules -F | grep -v / | wc -l
5541

フォルダサイズ

(New-Object -ComObject Scripting.FileSystemObject).GetFolder('node_modules').size
du --bytes ./node_modules
78565449

ファイル数もファイルサイズもなかなかの数値です(これでもwebpackよりは格段にマシですが)。SSDから別ディスクへのコピーや移動はお茶を二口くらい飲めそうですし、HDDにインストールした場合ファイル操作はあまりやりたくないですね。パッケージのダウンロードは圧縮されているとはいえ、モバイルネットワークでnpm iはちょっと避けたくなります。

ちょっとしたアプリを作るのに75MiB使うとすると、npmではプロジェクトごとにインストールすることになるので、14個も作れば1GiBになります。実際には各アプリで使うモジュールを更にインストールするので、ベースとなるサイズは少しでも落としておきたいところです。

しっかり開発する場合はそれでいい(というかそうするべき)のですが、ちょっと触りたい場合にこれではげんなりしますね。・・・それぐらい小さいアプリなら、Reactなんか使わずに生JS書けよってツッコミが入りそうですがw

余談ですが、npm createした後のnpmのキャッシュは230MB程になっていました。

代替を探す

現在の依存関係を知ったところで、代わりになるものを探していきます。

Reactは、本体は依存無し、react-domも少量の依存関係なのでそのまま使っても良さそうですが、preactの方が軽量らしいので、今回はそちらを試してみます。

TSXは、TSXTypeScriptJavaScriptとトランスパイルしていくことになるので、これらをトランスパイルするツールを探します。

TypeScriptJavaScriptは公式ツールがありますが、TSXJSXのトランスパイルは、何らかのバンドルツールしか情報が見当たりませんでした。

tsc

TypeScript標準のトランスパイラでnpm i typescriptでインストールするとついてきます。主にTypeScriptJavaScriptにトランスパイルするのに使われています。TSXTypeScriptもできるらしいのですが、あまり情報がありません。

webpack

言わずと知れたバンドルツールでcreate-react-appで作成したプロジェクトで使われていました。すでに開発は事実上終了していて、後継のTurboPackへ移行が進められているようです。

TSXTypeScriptのトランスパイルは追加Loaderのインストールが必要です。

開発・本番として必要な機能が一通り揃っており、プラグインでの機能拡張も非常に豊富ですが、とにかくビルドが重いです。

babel

こちらも言わずと知れた、本番向けに自動でフォールバックコードを適用するツールです。TSXTypeScriptのトランスパイルもプリセットを追加インストールすることで可能です。

esbuild

Go製のバンドルツールです。ネイティブで動作し、Goの得意な並列処理というのもあってか非常に高速です。Viteも開発用ビルドの内部では、これが使われています。

TSXTypeScriptもネイティブでトランスパイルします。minify機能もありますが、rollupよりはサイズが大きくなる傾向にあるようです。

ちなみにnpmでインストールしなくても、esbuildの実行バイナリ1個があればコマンドラインで単独で動かすことが可能です。また、実行ファイル自体がnpmパッケージに含まれているので、node-gypなどでのビルドも必要ありません。

環境作成

ということで、今回の目的にはesbuildが近そうなので、esbuildで環境を作成してみます。

npm i preact
npm i -D esbuild

esbuildは実行時のオプションで色々設定することも可能ですが、ベースとなる設定はtsconfig.jsonで指定します。tsconfig.jsonは自動で読み取ってくれます。

以下のサンプルは、とりあえず必要最小限のみ記載しています。

{
	"compilerOptions": {
		"jsx": "react-jsx",
		"jsxImportSource": "preact"
	}
}

esbuildではwatch機能や簡易サーバ機能も内蔵されています。Viteでは本番向けはesbuildではなくrollupを使いますが、今回はesbuildで本番用もビルドします。

開発向け

import { context } from "esbuild";

const ctx = await context({
	entryPoints: ["./src/main.tsx"],
	outdir: "./dest",
	bundle: true,
	minify: true
});

await ctx.watch(); // watch機能
await ctx.serve({ servedir: "./dest" }); // 簡易サーバ機能

※コマンドラインで実行する場合

esbuild ./src/main.tsx --outdir=dest --bundle --minify --watch --servedir=dest

本番向け

import { build } from "esbuild";

await build({
	entryPoints: ["./src/main.tsx"],
	outdir: "./dest",
	bundle: true,
	minify: true
});

特に設定を変えない場合は、実質的にcontextを使うかbuildを使うかの違いしかありません。

※コマンドラインで実行する場合

esbuild ./src/main.tsx --outdir=dest --bundle --minify

なお、いずれの場合もHTMLファイルは自動でoutDirへコピーされないので、ビルドスクリプトにコピーするスクリプトを組むか、手動でコピーします。

動作確認

適当にコードを書いて動作確認します。

ちなみにesbuildはhtmlファイルのminify機能はありません。CSSファイルはTSX内でimportすると自動で1つにまとめたCSSファイルとして出力する機能があります(<link>タグや<style>タグを自動挿入する機能は無し)。

<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Example</title>
		<script src="./main.js"></script>
	</head>
	<body>
		<div id="root"></div>
	</body>
</html>
import { render } from "preact";

const App = () => {
	return (<p>Hello, preact!</p>);
};

document.addEventListener("DOMContentLoaded", () => {
	const root = document.getElementById("root");
	if (root) {
		render(<App />, root);
	}
});

Hello, preact! が画面に表示されれば成功です。

おまけ

今回動かしたソースは以下のURLにあります。

https://github.com/Raintensity/JunkBox/tree/main/template/project/preact-minimal

カテゴリ: Javascript