バンドルサイズ
apps/synstate-dom-benchmark では、各ライブラリがアプリケーションにどれだけサイズを足すかも計っています。pnpm run bundle-size は、ベンチマークで時間を計っている実装を、アプリケーションとライブラリの組ごとに 1 つずつバンドルし、minify して圧縮します。以下の数値は、その BUNDLE-SIZE.md から埋め込んだものです。
- すべての export:ライブラリのエントリポイントの export をすべて残したもので、何も tree-shaking されません。synstate-dom は
synstate-domとsynstate、Solid はsolid-jsとsolid-js/web、Preact はpreactとpreact/hooks(Preact + Signals はこれに@preact/signalsを加えたもの)、React はreact、react-dom、react-dom/client、VanJS はvanjs-coreを数えています。あくまで上限であって、アプリケーションが実際に配信するサイズではありません。 - カウンター:数を表示して、クリックすると 1 足すボタンを、各ライブラリの流儀で書いたものです。アプリケーションのコードを書く前の時点で、ライブラリだけでかかるおおよその分にあたります。
- js-framework-benchmark の表:ベンチマークの表と、その 8 つの操作です。
- ローン・工程表・ズームのシナリオ:ベンチマークの 3 つのシナリオです。
どのバンドルも同じ Vite(Rolldown と Oxc の minifier)を使い、時間を計るときと同じ設定でビルドしています。どのライブラリも production ビルドと production 用の JSX ランタイムを使い、Solid のファイルは vite-plugin-solid を通します。なので tree-shaking の条件はどのライブラリでも同じですし、サイズを計るコードは時間を計るコードと同じです。実装どうしで共有しているコード(表の行を作る関数と、各シナリオのモデル)は、どの列にも含まれています。
Vite 8.3.5 でバンドルしました。バージョンは synstate-dom 0.9.0, synstate 8.0.0, solid-js 1.9.17, preact 10.29.8, @preact/signals 2.11.3, react 19.3.1, react-dom 19.3.1, vanjs-core 1.6.1 です。
minify して gzip
Section titled “minify して gzip”gzip はレベル 9 です。1 kB は 1,000 バイトで、小さいほど良い結果です。
| アプリケーション(kB) | synstate-dom | Solid | Preact + Signals | Preact | React | VanJS |
|---|---|---|---|---|---|---|
| すべての export | 12.8 | 14.7 | 9.3 | 6.2 | 67.7 | 1.2 |
| カウンター | 6.7 | 4.0 | 7.9 | 5.4 | 67.5 | 1.2 |
| js-framework-benchmark の表 | 8.8 | 5.1 | 8.7 | 8.3 | 68.3 | 2.0 |
| ローンのシナリオ | 7.6 | 4.2 | 8.5 | 6.1 | 68.1 | 1.8 |
| 工程表のシナリオ | 7.6 | 4.2 | 8.5 | 6.1 | 68.2 | 1.8 |
| ズームのシナリオ | 7.3 | 4.0 | 8.3 | 5.9 | 67.9 | 1.5 |
minify して Brotli
Section titled “minify して Brotli”Brotli は品質 11 です。
| アプリケーション(kB) | synstate-dom | Solid | Preact + Signals | Preact | React | VanJS |
|---|---|---|---|---|---|---|
| すべての export | 11.5 | 13.4 | 8.5 | 5.6 | 58.3 | 1.1 |
| カウンター | 6.1 | 3.6 | 7.3 | 4.9 | 58.2 | 1.1 |
| js-framework-benchmark の表 | 8.0 | 4.7 | 8.0 | 7.6 | 58.9 | 1.8 |
| ローンのシナリオ | 6.9 | 3.8 | 7.8 | 5.6 | 58.7 | 1.6 |
| 工程表のシナリオ | 6.9 | 3.8 | 7.8 | 5.6 | 58.7 | 1.6 |
| ズームのシナリオ | 6.7 | 3.6 | 7.6 | 5.4 | 58.5 | 1.4 |
結果の読み方
Section titled “結果の読み方”- カウンターの行が基準です。 その下のアプリケーションは、自分自身のコードと、使っているライブラリの部分の分だけそこから変わります。ここで計ったアプリケーションなら、どのライブラリでも、カウンターより下の行はどれもカウンターから数 kB 以内に収まります。その下の行もカウンターの行のライブラリの順位を保ちます。ただし、カウンターの行から下で互いに 1 kB ほどの差しかない synstate-dom と Preact + Signals は入れ替わることがあります。
- React は、アプリケーションが何であってもほとんどが react-dom です。reconciler と scheduler はコンポーネントが何を使うかによって分かれないので、行どうしの差は 1 kB ほどしかありません。
- VanJS は tree-shaking する余地のない小さな 1 モジュールなので、カウンターの時点でその全体が入ります。
- Solid は JSX をテンプレートにコンパイルして小さなランタイムを呼び出すので、カウンターでは
solid-js/webのごく一部しか使いません。すべての export はその数倍あります。 - Preact は小さな仮想 DOM で、Signals はそこにリアクティブなコアと、signal を DOM に束縛する統合部分を足します。Preact の表がすべての export より大きいのは、表の行が
preact/compatのmemoを使っていて、すべての export の行ではそれを数えていないからです。 - synstate-dom には、synstate の伝播のコア(グラフ、深さ順の伝播、スコープ)、自身の JSX ランタイムと束縛の表、そして両者が ts-data-forge と ts-std-forge から import するヘルパーが含まれています。すべての export 以外のどの行でも、React の次に大きいのは synstate-dom と Preact + Signals です。ただ、すでに synstate を使っているアプリケーションなら、そのコアの分は一度払えば済みます。また、モデルがどの列でも ts-data-forge を使うシナリオでは、そのヘルパーを synstate と共有します。
- すべての export の行では、React の次に synstate-dom と Solid が大きくなっています。synstate は RxJS と同じようにオペレーターを一揃い export していますが、アプリケーションに残るのは import したものだけです。
pnpm run ws:build # synstate-dom と synstate は dist/ から import しますcd apps/synstate-dom-benchmarkpnpm run bundle-size # BUNDLE-SIZE.md を書き換えますcd ../synstate-dom-docspnpm run gen:bundle-size # BUNDLE-SIZE.md をこのページに埋め込みますサイズはソースとインストールされたバージョンだけで決まるので、1 回実行すれば十分です。CI では実行していないので、BUNDLE-SIZE.md は最後に実行したときのツリーでの値です。