Astroの「アイランドアーキテクチャ」とは? Vueを組み込んだ実例から仕組みを理解する

「アイランドアーキテクチャ」を理解するために
Astroはアイランドアーキテクチャと呼ばれる新しいフロントエンドアーキテクチャパターンを提唱し、普及させました。アイランドアーキテクチャでは、ページの大部分を高速な静的HTMLとしてレンダリングし、インタラクティブ性やパーソナライズが必要な箇所に小さなJavaScriptの“アイランド”を追加します(たとえば、画像カルーセルなど)。これにより、多くのモダンJavaScriptフレームワークで問題となる巨大なJavaScriptペイロードによるレスポンス低下を防げます。
上記は公式HPのアイランドアーキテクチャの項からの引用である。Astroには「Zero JS by Default」というブラウザに送信するJavaScriptをゼロにする思想で作り込まれているが、それでも「この部分はどうしてもJavaScriptで動きを出す必要があるんだよな」というニーズに応える必要が出てくる。そういった "どうしても動かしたい部分" のみを切り出してアイランドとして追加することができる……これがAstroにおける「アイランドアーキテクチャ」の概要である。
この概要について理解を深めるためには、まずレンダリングというものについて理解する必要がある。レンダリングについて理解しようと思ったらブラウザについて知らないと……と延々の入れ子になりかねないが、一旦、レンダリングとは何かについて知ることのみ本記事では語らせていただく。
そもそも我々が見ているウェブサイトというのは、サーバーから取得したデータをブラウザが頑張って描画したものである。ここでいう "データ" というのは、HTMLというマークアップ言語で書かれた階層構造情報や、CSSといったレイアウト情報に加え、様々な動的要素を実現するスクリプト言語・JavaScriptで記述された情報などを含んでいる。これらの構成情報を組み合わせてウェブページを描画することをレンダリングという。
そんなレンダリングにおいて、HTMLやCSSといった静的要素について、処理するのにはさほど時間はかからない。しかし、JavaScriptがここに加わると時間が一気にかかるようになる。その辺りの技術的要因については別途記事を作成する予定である。
とにかくここで大事なのは、JavaScriptがあるとレンダリングに時間がかかる、ということだ。そこで考えられたのが、動的に変化する部分だけを別コンポーネントで制御する「アイランドアーキテクチャ」という考え方である。静的HTMLという大海原に、動的部分がポツンと浮いているような光景をイメージしてもらえば、この名称も分かりやすいのではないだろうか。
「アイランドアーキテクチャ」をどう実現するか
考え方については伝わったと思う。問題はAstroではどのようにして「アイランドアーキテクチャ」を実現させているのかということだ。皆さんが今見ているサイトはAstroを使って作成されており、実際にAstroアイランドという仕組みを使っている。実際にコードで書いてみた所感を交えつつ、整理していきたい。
下記は本サイトにおいて「アイランド」を実際に使用している構成である。ページ毎にsrc/pages/*.astroを作成し、src/layouts/*.astroに配置したヘッダーやフッター、タイトルなどのデザインを共通で使っている。下記に示したものは全てHTML/CSSによる静的要素となっている。
- ページ骨格・静的HTML:
src/pages/*.astro+src/layouts/*.astro - 操作が必要な部品:
.vueコンポーネントをアイランドとして埋め込み
動的な要素については上記で示す通りVueを使って実現しており、分かりやすいページでいうと「ゲーム一覧」ページかと思うので、そちらベースに解説していきたい。
ゲーム一覧画面は下記のようなファイル構成で作成されている。src/components/GameList.vueは拡張子を見てもらえば分かる通りvueで作成されており、DBで管理しているゲームのリストをAPIを用いて取得して動的に更新できるようにしている。「vueって何?」という方は、UIフレームワークの1つだと覚えてもらえれば良い。
| ファイルパス | 役割 | 種類 |
|---|---|---|
src/pages/works/games/index.astro |
ページのルーティングと全体構造の定義 | Astro Component |
src/layouts/PageLayout.astro |
ヘッダー・フッターを含む共通レイアウト | Astro Component |
src/components/Header.astro |
ナビゲーションメニュー(静的) | Astro Component |
src/components/GameList.vue |
ゲーム一覧の表示・絞り込み・ページネーション | Vue Component (Island) |
src/lib/api/works-games.ts |
Backend APIとの通信ロジック | TypeScript (API Client) |
| ここで考えて欲しい疑問が2つある。 |
- そもそもVueファイルを組み込んで動かすことができるのか?
- index.astroからどうやってGameList.vueを呼び出しているのか?
そもそもVueファイルを組み込んで動かすことができるのか?
結論からいうと、動かすことは可能である。「このサイトが皆さんにお届けできて、実際に動いているから」という根拠を挙げたところで納得感は薄いだろうから、公式サイトからの引用をベースに解説をしていきたい。
アイランドは常にページ上の他のアイランドから独立して実行され、複数のアイランドを同一ページに配置できます。コンポーネントコンテキストが異なっていても、クライアントアイランド同士は状態を共有し、相互通信が可能です。 この柔軟性により、AstroはReact、Preact、Svelte、Vue、SolidJSなど複数のUIフレームワークをサポートします。アイランドは独立しているため、1ページ内で複数フレームワークを混在させることもできます。
Astroは公式インテグレーションとして @astrojs/vue や @astrojs/react を提供している。これらをプロジェクトに導入することで、Astro側がビルド時に自動でVueのコンパイラを呼び出し、コンポーネントを解釈してくれる仕組みになっているのだ。
さらに驚くべきは、「1ページの中でVueとReactを混在させることすら可能」という点だ。コンポーネント同士が「独立した島」として動いているからこそ、フレームワークの壁を越えた自由な構成が実現できる。とはいえ具体的にどのような実装で実現できるのか? その難易度が高い場合、どんなに複数のUIフレームワークをサポートしていたとしても嬉しくない。その話を次にしたい。
index.astroからどうやってGameList.vueを呼び出しているのか?
index.astroのコードは短いため、全文をここに記載する。astroのコードの書き方などについて知らないという方でも、HTMLさえ分かれば、ある程度は感覚で理解できるかと思う。
---
import PageLayout from '../../../layouts/PageLayout.astro';
import PageHeading from '../../../components/PageHeading.astro';
import GameList from '../../../components/GameList.vue';
---
<PageLayout
title="ゲーム一覧"
description="制作したゲームの一覧です。使用技術・制作期間・リンクを掲載しています。"
>
<div class="container-narrow py-12 pb-0">
<PageHeading title="ゲーム一覧" class="mb-8" />
<p class="text-main-muted mb-10">
完成品と開発過程をブログ形式で掲載。GitHub・プレイ動画などへのリンクも用意しています。
</p>
</div>
<GameList client:load />
</PageLayout>
注目して欲しいのは<GameList client:load /> の部分である。GameListは動的に動かしたい要素をVueで記述したコンポーネントである。そのコンポーネントがブラウザ上で「どのように動的に機能するか」を示しているのが、client:load(クライアントディレクティブ)という属性である。こちらが存在しない場合、該当のコンポーネントはHTMLの静的要素として解釈されることとなる。
また、loadの部分については動作のタイミングに応じて、下記のようなものが用意されている。動的な要素が実際に画面上に描画されるタイミングについて、調整可能ということである。
| ディレクティブ | 動作のタイミング | 主な用途 |
|---|---|---|
client:load |
ページロード時に即座にJSを読み込んで実行 | 画面を開いてすぐ使う重要パーツ(検索窓など) |
client:visible |
ユーザーが画面をスクロールして要素が見えたらJSを読み込む | 画面下部にあるカルーセルや動画プレイヤー |
client:media |
指定したCSSメディアクエリを満たした時だけJSを読み込む | モバイル表示時のみ動かすハンバーガーメニュー |
client:only="vue" |
サーバーレンダリング(HTML生成)すらスキップし、ブラウザ上でのみ描画 | APIキーやブラウザ固有API(localStorage等)に依存する |
まとめ
Astroの「アイランドアーキテクチャ」について整理すると、その本質は「必要なところだけピンポイントでJavaScriptの命を吹き込む」ことにある。
- ページ全体(90%): Astroの超高速な静的HTMLで描画する(静的な海)
- 動的パーツ(10%):
client:*を使ってVueやReactで動かす(動的な島)
ページ全体を巨大なJavaScriptで動かす「従来のモダンWebアプリ(SPA)」とは異なり、無駄なJSをブラウザに読み込ませないため、表示速度は圧倒的に速くなる。
「高速なWebサイトを作りたいが、動的なUIもあきらめたくない」という開発者のワガママを極めてエレガントに解決してくれる仕組み、それこそがAstroのアイランドアーキテクチャなのだ。



