JavaScriptの「肥大化」という悩み
2年前、当時最も人気のあるフレームワークの一つであったNext.jsを使ってニュースサイトを構築しました。最新のツールを使っているという自負がありましたが、Google PageSpeed Insightsで測定したところ、モバイルのパフォーマンススコアはわずか55点でした。
ブラウザは、ドロップダウンメニューとページ下部の登録フォームを表示するためだけに、200KB以上のJavaScriptを読み込み、実行しなければなりませんでした。わずか数行のテキストを読むために、ユーザーに重いフレームワークを強制的にダウンロードさせるのは、非常に悪い体験です。これは、現代の多くのウェブプロジェクトが直面している課題です。
Hydration:静かにパフォーマンスを奪う「犯人」
なぜ優れたフレームワークを使っているのにウェブサイトが遅いのでしょうか?主な原因はHydration(ハイドレーション)にあります。ReactやVueでは、サーバー側でHTMLをレンダリングしたとしても、ブラウザはコードを再実行し、イベントリスナーを登録するために、すべてのJavaScriptファイルをダウンロードする必要があります。
このプロセスはCPUを消費し、Total Blocking Time(TBT)を増加させます。実際、ブログやランディングページのコンテンツの約80%は静的なものです。インタラクション(操作)を必要としない部分にまでJavaScript의 処理を強いるのは、リソースの大きな無駄です。
Islands Architecture:「分割統治」の考え方
この状況を打破するために登場したのが、Islands Architecture(アイランド・アーキテクチャ)です。ウェブサイト全体を巨大なJavaScriptの塊にするのではなく、ウェブサイトを「静的なHTMLの海」と捉えます。
その海の中に、ショッピングカートや検索バーなどのインタラクティブなコンポーネントを、JavaScriptを含む小さな「島(Islands)」として配置します。これらの島だけがコードをダウンロードし、残りの部分は純粋なHTMLとなります。このアプローチにより、ページの応答時間が大幅に短縮されます。
Astro.js – Zero-JS時代を切り拓くフレームワーク
Astro.jsは、現在このIslands Architectureを最も効果的に採用しているフレームワークです。React、Vue、Svelte、Solidなどを一つのプロジェクト内で混在させることができます。デフォルトでは、明示的に指定しない限り、Astroは**Zero JavaScript**(JavaScriptゼロ)で出力します。
ステップ1:プロジェクトのクイックスタート
ターミナルに以下のコマンドを入力して開始しましょう:
npm create astro@latest
デフォルトのオプションを選択した後、ディレクトリに移動して開発環境を起動します:
cd project-name
npm run dev
ステップ2:マルチフレームワークの統合
Astroは非常に柔軟です。チーム内にReactが得意な人とVueが好きな人が混在していても、コマンド一つで両方を統合できます:
npx astro add react vue
システムが自動的にastro.config.mjsを設定します。これで、異なるライブラリのコンポーネントを並行して使用することが非常に簡単になります。
ステップ3:インタラクティブな「島」の構築
違いを確認するために、Reactでカウンターコンポーネントを、Vueで通知コンポーネントを作成してみましょう。
src/components/ReactCounter.jsx
import { useState } from 'react';
export default function ReactCounter() {
const [count, setCount] = useState(0);
return (
<div className="p-4 border rounded">
<p>Reactカウンター: {count}</p>
<button onClick={() => setCount(count + 1)} className="bg-blue-500 text-white px-2">
カウントアップ
</button>
</div>
);
}
ステップ4:ディレクティブによるJavaScriptの制御
src/pages/index.astroファイル内で、通常通りコンポーネントを呼び出すと、Astroはそれらを静的なHTMLとしてレンダリングします。JavaScriptが付随しないため、ボタンは機能しません。インタラクティブ機能を有効にするには、client:*ディレクティブを使用します:
---
import ReactCounter from '../components/ReactCounter';
import VueNotification from '../components/VueNotification.vue';
---
<html lang="ja">
<body>
<h1>Astro Islands</h1>
<!-- アイランド 1: ページロード時に即座にJSを読み込む -->
<ReactCounter client:load />
<div style="height: 100vh;"></div>
<!-- アイランド 2: ユーザーがスクロールして表示された時のみJSを読み込む -->
<VueNotification client:visible />
</body>
</html>
これらのディレクティブにより、非常に詳細な制御が可能になります:
client:visible: コンポーネントがユーザーの視界に入った時のみJSを読み込みます。これはフッターなどの最適化に非常に効果的です。client:idle: ブラウザのメインスレッドが空いた時にJSを読み込みます。client:only: サーバーレンダリングをスキップし、クライアントサイドのみで実行します。
実測結果:200KBから10KBへ
以前、50個のコンポーネントを含むランディングページをリファクタリングしたことがあります。Next.jsを使用していた時は、メインのJSファイルが約200KBあり、LCP(Largest Contentful Paint)は2.5秒かかっていました。
Astroに移行し、必要な部分にclient:visibleを適用したところ、初期読み込みのJS総量は10KB以下に減少しました。Lighthouseのスコアは完璧な100点になり、TTI(Time to Interactive)もほぼ瞬時になりました。
どんな時にAstroを選ぶべきか?
Astroはすべてのケースに適した解決策ではありません。JiraやFacebookのような複雑なダッシュボードを構築する場合は、引き続きNext.jsやReactのSPA(Single Page Application)を使用することをお勧めします。しかし、以下のようなプロジェクトではAstroが最良の選択肢となります:
- ニュースサイト、ブログ、または個人のポートフォリオ。
- 企業サイト、販売用ランディングページ。
- 技術ドキュメントサイト(Documentation)。
- SEOと初回読み込み速度を優先するEコマースサイト。
Islands Architectureを活用することで、モダンな開発体験を維持しながら、エンドユーザーに最高のスピードを提供することができます。

