Astro(Island Architecture)とNext.jsのパフォーマンス比較
概要
Island ArchitectureをベースにしたAstroというフレームワークがどういうユースケースで採用するとよさそうかを調べていく中で、AstroとNext.jsとでパフォーマンスの比較を行ってみました。
Astroのドキュメントにも、どういう時に採用するといいかは書かれていますが、実際に検証してみることで本当にそうなのかというところが理解深まった気がします。
補足
- Astro
- https://astro.build/
- https://docs.astro.build/en/concepts/why-astro/
- 特徴
- Island Architecture
- MPA
- Island Architecture
Astroを採用するとよさそうなケース
Astroの公式ドキュメントに下記のように記載されています。
Astroは、コンテンツが豊富なウェブサイトを構築するために設計されています。
// 中略
これに対して、最近のほとんどのWebフレームワークは、Webアプリケーションを構築するために設計されています。
// 中略
これはAstroを理解する上でもっとも重要な違いの1つです。Astroはコンテンツにフォーカスするという独自の立場からトレードオフを行い、アプリケーションにフォーカスしたWebフレームワークでは実装する意味がないような比類ないパフォーマンス機能を提供します。
-- https://docs.astro.build/ja/concepts/why-astro/#コンテンツ重視
要するに、下記の選択を取ると良いパフォーマンスを発揮できるよということだと思います。
- 動的なコンテンツが多いWebアプリケーション(Twitterなど)
- -> Next.jsなど
- 静的なコンテンツが多く、かつ部分的に動的なコンテンツを組み込みたい(zennなど)
- -> Astro
パフォーマンスの比較
下記の2パターンのページをAstroとNext.jsで作成し、配信されるコンテンツの合計サイズ、ページロード時のパフォーマンスを計測して比較しました。
- パターン1: 動的なコンテンツ(Counter)と1000件の静的なコンテンツを表示するページ
- Astroが得意としているようなページ
- 静的なコンテンツが主。部分的に動的コンテンツがある
- 動的なコンテンツ: よくあるようなCounter
- パターン2: 1000件の動的なコンテンツを表示するページ
- Astroが得意としていないようなページ
- 動的なコンテンツ: 項目ごとに削除ボタンを配置し、ボタンにクリックイベントを定義(clickされたlog出力するだけ)
- Astroのコードでは、1000件のIslandがあることになります
検証コードは下記にあげています。
- Astro
- Next.js
結論
Astroのドキュメント通りの結果になりました。
静的なコンテンツが多い場合はAstroを採用すると、より良いパフォーマンスが出せそうです。
前提
比較していく上で、下記の前提で実施しています。
- それぞれproduction buildしてSSRでローカルから配信
- それぞれgzipを無効化
- それぞれ実装したページコンテンツ自体の差異がないように同じReact Componentを利用
- Next.jsのversion: 13.2.4
- Astroのversion: 2.1.3
比較結果: パターン1(動的なコンテンツ Counterと1000件の静的なコンテンツを表示するページ)
配信されるコンテンツの合計サイズ数は下記のように、Astroが小さくなっています。
Astro | Next.js | |
---|---|---|
サイズ | 253kB | 545kB |
結果 |
Astroは動的コンテンツ部分のみのhydrationが必要なのに対し、Next.jsはページ全体のhydrationが必要になるため、ソースサイズが増えることは納得できます。
静的なコンテンツの件数を増減させ、その時の配信サイズの変化を見ることで、hydrationのボリューム分サイズが増えることに対しより納得感が得られました。
Astro | Next.js | |
---|---|---|
サイズ(100件) | 158kB | 293kB |
サイズ(1000件) | 253kB(+95kB) | 545kB(+252kB) |
実際に配信されたソースを確認すると、SSRされたコードと、hydration用のコードが増加していたため、Astroに比べてNext.jsはhydrationのボリューム分配信するサイズが増えそうです。
また、ページロードのスクリプト実行時間もNext.jsの方が長くなっています。
おそらくhydarationする部分が多いためだと思われます。
Astro | Next.js | |
---|---|---|
スクリプト実行時間 | 64ms | 109ms |
結果 |
ちなみにlighthouseの結果は次のとおりになりました。
Astro | Next.js | |
---|---|---|
結果 |
比較結果: パターン2(1000件の動的なコンテンツを表示するページ)
配信されるコンテンツの合計サイズ数は下記のように、Next.jsが小さくなっています。
Astro | Next.js | |
---|---|---|
サイズ | 722kB | 502kB |
結果 |
ページロードのスクリプト実行時間は明らかにAstroの方が長くなっています。
※Astroのコードでは1000件のIslandがあることになります。
Astro | Next.js | |
---|---|---|
スクリプト実行時間 | 3592ms | 102ms |
結果 |
このように、Astroのドキュメントにも記載しているとおり、Islandが多い場合はAstroは厳しそうです。
ただし、Astro側ではIslandごとにhydrateタイミングを制御できます。
client:visible
を指定すると、コンポーネントがビューポートに入るとhydrateされるため、大きく改善します。 参考
まとめ
Astroのドキュメントに記載しているとおり、静的なコンテンツが主で、部分的に動的なコンテンツを組み込みたい場合はAstroを選択するとよさそうに感じました。
- 静的なコンテンツが主の場合、hydration用のコードを動的コンテンツ部分のみに抑えることができるため、配信するサイズとページロード時のスクリプト処理時間を小さく抑えることができそう。
- 動的なコンテンツが主の場合(Islandの数が多くなる場合)、ページロード時のスクリプト処理時間が大きくなる。client directiveの活用でそこは小さく抑えられそうだが、あえてAstroを選択する理由はパフォーマンスの面ではなさそう。
Discussion