各 JavaScript フレームワークの TodoMVC サイズ比較(翻訳)
JavaScript フレームワークのサイズを概算するのは、実はとても難しいことです。
ランタイムのサイズを正確に把握することさえ難しいのです。あなたは bundlephobia.com をよく見るかもしれませんが、そこに書かれているサイズは誤解を招く可能性があります。tree-shaking すれば、すべてのコードが含まれるわけではありません。Bundlephobia では svelte/motion
や preact/hooks
のようなサブモジュールも含まれていません。
ランタイムのサイズだけではなく、コンポーネントコードのサイズも重要です。すべてのコンポーネントが同じではありません。フレームワークごとにテンプレートのコンパイル方法は異なります。Vue の開発者である Evan You 氏は、Svelte と Vue の比較を行いましたが、非常に分かりやすかったです。
私は彼のプロセスと方法論を採用し、Preact、React、Solid にも適用することにしました。では、始めましょう。
私は Solid の作者なので、偏りがあるかもしれないという点に注意してください。できる限り平等になるように心がけています。
構成
このテストでは、TodoMVC でのコンポーネントコードとライブラリ(ベンダー)バンドルのサイズを調べます。どのフレームワークにもバージョンがあり、要件がきちんと整理されているので、それぞれが相対的に同じになります。
最初は公式のデモだけを使うことを検討しましたが、Preact や React はいまだにクラスコンポーネントを使っていてサイズが大きく、モダンな開発の典型とはいえませんでした。これは、Preact のライブラリサイズの増加(3.81kb → 4.39kb)を意味します。Preact はメインパッケージの一部として hooks を含んでいませんが、今回のテストには間違いなく価値があります。結局、満足のいく hooks の実装が見つからなかったので、Solid の実装をベースにした独自の実装を使いました。すべての実装はこちらでご覧いただけます。
ベンダーチャンクは、Vite から取得しています。Vite は今回テストしたすべてのフレームワークをサポートしています。コンポーネントコードについては、Vue、Svelte、Solid の REPL と、Terser の REPL を使ってミニマイズしました。Preact と React については、rollup を使ってコンパイル済みのコードを生成しました。
このプロセスは、私が通常行っているベンチマークよりもはるかに厳格なものではありません。正直なところ、コーディングスタイルや利用可能なデモコードによって、それなりにばらつきがあります。しかし、それでもほぼ一致していると思います。
結果
最初のステップは、コンポーネントのサイズとそれぞれのベンダーコードを取得することです。TodoMVC は、基本的な状態の処理、条件付きレンダリングやループレンダリング、フォーム、さらにはローカルストレージへのシリアライズを含んでいるので、かなり合理的な例となります。そのため、各フレームワークの基本的なサイズについて、Bundlephobia よりもはるかに良いアイデアを得ることができました。
Preact | React | Solid | Svelte | Vue | |
---|---|---|---|---|---|
component size (brotli) | 1.21kb | 1.23kb | 1.26kb | 1.88kb | 1.10kb |
vendor size (brotli) | 4.39kb | 36.22kb | 3.86kb | 1.85kb | 16.89kb |
一般的に、ミュータブルはイミュータブルよりもサイズが小さく、VDOM なしのライブラリはテンプレートのために多くの JavaScript を生成します。Vue のコンポーネントは、JSX ライブラリや Svelte を差し置いて、最も少ないコードを生成しています。
Svelte のランタイムは本当に小さくて 1.85kb です。Preact のコアは Solid よりも小さいかもしれませんが、Preact に hooks を付けると、最終的には Solid[1] の方が小さくなります。
これを使って、フレームワークごとに N 個の TodoMVC コンポーネント + ベンダーチャンクのサイズを算出するのは簡単です。
1 | 5 | 10 | 20 | 40 | 80 | |
---|---|---|---|---|---|---|
Svelte | 3.73kb | 11.25kb | 20.65kb | 39.45kb | 77.05kb | 152.25kb |
Solid | 5.12kb | 10.16kb | 16.46kb | 29.06kb | 54.26kb | 104.66kb |
Preact | 5.60kb | 10.44kb | 16.49kb | 28.59kb | 52.79kb | 101.19kb |
Vue | 17.99kb | 22.39kb | 27.89kb | 38.89kb | 60.89kb | 104.89kb |
React | 37.45kb | 42.37kb | 48.52kb | 60.82kb | 85.42kb | 134.62kb |
Svelte がリードしてスタートしたものの、すぐに Solid に抜かれてしまい、その後は Preact に王座を譲ります。Preact は、テーブルのかなりの部分で最小となり、最終的には Vue が最小になります。
つまり、変曲点を表にすると:
Svelte | Solid | Preact | Vue | React | |
---|---|---|---|---|---|
Svelte | - | 3.2 | 3.8 | 19.3 | 52.9 |
Solid | - | - | 10.6 | 81.4 | 1078.7 |
Preact | - | - | - | 113.6 | - |
Vue | - | - | - | - | - |
React | - | - | - | - | - |
これは、各フレームワークが次のフレームワークよりも大きくなるポイントです。TodoMVC が 0~3 個の場合、Svelte が最小です。3 から 10 までは Solid が最小。10~113 なら Preact で、113 以上の場合 Vue です。
Preact と Vue は React と交差することはなく、Solid でも TodoMVC が約 1080 個以降にしか交わりません。全体的に見て、これはデモやベンチマークで見られるものとかなり一致しています。Svelte は Hello World や TodoMVC では常に最小で、Solid は「現実の世界」でのデモ[2] や時代の流れに沿ったシンプルなサイトのタイプに向いていて、Preact はさらに大規模なものに適しています。
分析
TodoMVC は 1 つのコンポーネントとしては大きい方で、一般的には同じことを 3~4 つのコンポーネントに分けて実装するので、これだけのコンポーネント数が必要とは限りません。しかし、各フレームワークにはそれぞれスイートスポットがあることは容易に理解できます。
サイズが最も影響するのは、初期ページロード時です。他のコード分割された route は必要に応じて遅延ロードされますが、初期ページロードはすべてのサイトが事前に負担するコストです。Addy Osmani の The Cost of JavaScript シリーズや Alex Russell の Can You Afford It?: Real-world Web Performance Budgets に書かれている考え方を支持するのであれば、初期ページロードの JavaScript を 130kb 以下に抑えることを目指すべきでしょう。
SPA の場合、この予算にはデータフェッチ、状態ライブラリ、ルーターなどが含まれます。ほとんどのフレームワークでは、さらに 20~25kb の JS が必要になることも珍しくありません。Svelte、Solid、Vue のようなリアクティブなものであれば、状態管理が組み込まれている場合がありますが、それでもサードパーティのフォーマット用ユーティリティーライブラリを考慮すると、フレームワークとコンポーネントコードは 100kb 未満である必要があります。
その予算だと、各フレームワークはいくつの TodoMVC を許容するでしょうか?
React | Vue | Preact | Solid | Svelte | |
---|---|---|---|---|---|
10kb | - | - | 4.6 | 4.7 | 4.3 |
20kb | - | 2.8 | 12.9 | 12.4 | 9.7 |
40kb | 3.1 | 21 | 29.4 | 28.7 | 20.3 |
70kb | 27.5 | 48.3 | 54.2 | 52.5 | 36.3 |
100kb | 51.9 | 75.6 | 79.0 | 76.3 | 52.2 |
さて、100kb では React と Svelte はほとんど同じです。Vue、Preact、Solid はそれよりも利用可能な予算がほぼ 33% 多く、互いに隣接しています。しかし、それは上限の話です。40kb では Preact と Solid は、Vue や Svelte に対する優位性を同じくらい持ち、かなり重いサイトを提供できます。この範囲では React は選択肢にすら入りません。
Alex 氏の目標は、低速のデバイスとネットワークで TTI を 5 秒にすることでした。e コマースのような一部の業界では、目標は 3 秒など、より高度にすべきです。70kb - 25kb = 約 45kb の予算となります。React のような大規模なライブラリがどうやって対抗できるでしょうか?
React Server コンポーネントは、React の現在のランタイムに加えて約 8kb 追加しますが、すでにこの会話からは除外されています。ルーティングの必要性を排除した Astro のようなマルチページのメタフレームワークと、おそらく他のサードパーティのライブラリは、おそらくかろうじて十分です。しかし Astro のような素晴らしいツールであっても、JavaScript をロードする際には、約 9kb の容量が必要になります。
しかし、それ以外ついては、もっと余裕があります。シンプルなサイトで、1 つのページに 5~10 個のパーツしかないような場合では、どんな選択肢でも問題ないでしょう。Preact、Svelte、Solid のような小さなライブラリのフル SPA でも、十分に満足できる範囲です。
まとめ
重要な範囲では、すべてのフレームワークはほぼ同程度です。Svelte は大規模なアプリの場合、最終的に多くのページでより多くの JavaScript を読み込む可能性がありますが、他のフレームワークは大規模な場合でもそれを感じさせないものとなっています。
全体的に見ると、サイズに関しては Preact が勝っています。Solid も十分に近づいており、その差は気にならないでしょうが、Preact は頷くに値します。React は小規模なターゲットやパフォーマンス重視のターゲットには向いていませんが、アプリが十分に大きくなれば、その大きさは気にならなくなります。Vue はこの範囲のちょうど中間に位置していますが、大規模なアプリで送信される JavaScript が最小になる可能性があります。
この比較はかなり大まかなものであり、あくまでも概算であることを覚えておいてください。もっと多くのライブラリを見るために時間を割けなかったことを後悔しています。当然のことながら、TodoMVC の例の多くは MVC スタイルで書かれていてこの比較は向いていなかったり、Redux のようなストアを使ったりしています。自分でたくさん書く準備はしていませんでした(Preact と React で十分でした)。なので、これで我慢するしかありません。
うまくいけば、次にサイズの話が出てくるまでの間、これで十分に考えることができるでしょう。あるいは、Marko と Qwik がブラウザに送信される JavaScript フレームワークのコードを測定する方法を完全に破壊していることを考えると、その頃には全く異なる会話になるでしょう。
この記事で使用されている TodoMVC の例の完全なソースはこちらにあります。
本文ここまで。
誤訳などありましたらコメントいただけると助かります。
Discussion
翻訳ありがとうございます!
実はこの計算と検証を私も以前行ったことがあって、そのときに思うことがあったのでそれをこちらにまとめました。よろしければお読みになってください!