2022年10月の「TechFeed Night #4 - フロントエンドアーキテクチャを語る」で反響が大きかったセッション「アイランドアーキテクチャとは」を担当されたりぃ@leader22さんに、アイランドアーキテクチャの概要と今後の展望について、TechFeed CEOの白石俊平@shumpeiがお話を伺いました。アイランドアーキテクチャを採用した静的サイトビルダー「Astro」についても具体的な事例をもとに紹介します。
今回話を伺ったエキスパート
フォローしよう!
–まずは、アイランドアーキテクチャじたい、あまり広く知られたものではないと思うので、アイランドアーキテクチャの概要をお伺いしたいと思います。アイランドアーキテクチャとは何でしょうか?

前回の「TechFeed Night #4」で発表した資料と合わせて説明します。7ページ目ですね。

アイランドアーキテクチャの名が示す通り、ページを構成する要素として、JSが必要な(=動的)カルーセルとかポップアップなどを「島」に見立てて、その他のヘッダーだったり、テキストや画像のようなJSのいらない(=静的)ものは「海」に見立てて構成する考え方になります。
これらはサーバーサイドにリクエストが来たときに生成してもいいし、事前に生成しておいてCDNに上げておいてもいいんですけど、SPAのように動的に画面を作っていくのではなく、事前に生成されたものをクライアントに返すという前提で、ダイナミックな島はクライアントで動かすという構造になってます。
--アイランドアーキテクチャはなぜ注目されているのでしょうか?

資料の9ページをもとに説明します。

基本的にWebページって、動的な要素もあれば静的な要素もあるという組み合わせでできていると思うんですけど、ただ、最近流行りのフレームワークはそもそもSPAを作るために作られたものなので、100%静的なページや、ほんの一部分動くといったものでも、必然的にJSのサイズがすごく増えてしまいます。
たとえば「Hello World」という文字列を表示するページのためだけに、なぜ無駄に81.7kのJSをダウンロードして実行しないといけないのかという話になります。ゴリゴリのSPAになっていくとか、ページ内に動的な要素がたくさんあるというのであれば納得なんですけど、そうでない場合は要らないです。この問題を何とかできないかなということで、アイランドアーキテクチャが生まれた感じです。
--なるほど。ではアイランドアーキテクチャというのはどのように動作するのでしょうか?とくに、静的な部分と動的な部分を分けて動かすしくみなどをお聞きしたいのですが。

そうですね。これも見たほうが早いので、この「Offersデジタル人材総研」のサイトがちょうど良い感じなので、これで見ていきましょう。

このサイトって、画面が狭くなったときだけメニューが出るんですけど、画面が広いときはないんですね。


メニューがないときは、ページをロードしてもメニューに関するJSファイルがほぼないんですよ。でも画面を狭くして、メニューが必要になったらJSがロードされてますね。このように必要になったタイミングで「クリックしたらメニューが開く」という部分のJSを取得している感じです。それ以外の要素に関してはJSが要らないので、それ以外のJSは落ちてこなかったというということになります。

--なるほど…これはどうやって実現しているのでしょうか?

コードは見られないので、Astroのドキュメントを見てみます。

--ドキュメント自体もAstroで動いてるんですか?

たぶんそうだと思います。GitHubでコードも確認できます。
Astroのコードをどう書くかなんですけど、基本的なコードはReactのコードなどで見たことがあるように、コンポーネントを組み合わせて書いていくのですが、JSが欲しいところに関しては、こんなふうに client:◯◯
と書きます。

さっき紹介したサイトに戻ると、ふだんは見えていない部分(メニュー)が見えるようになったら(JavaScriptファイルを)読み込んでほしいという場合には client:visible
が使われています。


Astroのリファレンスに書いてある通り、ビューポートに入ったら処理されると。内部実装は IntersectionObserver
が使われていて、画面に入ったことを検知して、そのコンポーネントに必要なコードを取ってきて動かすということをしてくれている感じです。

実際に IntersectionObserver
監視の対象となるコードは最初の段階からロードされています。これもページをビルドするときに、必要なものがなければ出ないので、必要なものだけロードされます。client:visible
のように画面に入ったら…というような優先度の低い指定以外にも、優先度を高く、ページをロードしたら即座に処理させることもできます。client:load
や client:idle
がそのための指定ですが、これらをいっぱい書くようなケースであれば、それは普通にSPAを作るのと大差ないかなと思います。
そのほかでは client:media
はCSSのMedia Queryで指定できます。

(要素内に)条件を書けると…おもしろいですね!astroは基本的にはサーバーサイドで動作するんですか?

そうですね。サーバーサイドでレンダリングして、クライアントに返したいので、基本的にはSSRすることになります。これらのコンポーネントはReactなどのフレームワークを使って書いてもいいんですけど、*.astro
というファイルでもJSXライクなコードは書けるので、クライアントで動かすJavaScriptがないのであれば、Reactなどを使う必要はないです。ただ、たとえばVueのシンタックスが好きだから(map
ではなく、v-for
でリストを書きたいとか)といった理由で使ってもいいです。
基本の流れとしては** *.astro
ファイルにJSXでHTML/CSSを書いて、クライアントでJSを動かしたいやつが出てきたら、ReactやVueやSvelteなどを選んでコンポーネントにして、さっきの client:load
とかを指定していく**という感じです。
--まだ理解が追いついてな部分もあるのですが、Astroは外部のフレームワークを必要とするプロダクトではない、ということでしょうか?

そうですね。なくてもいいんですけど、Astro単体だとクライアントにJSを出力できないので、ただの静的サイトジェネレータになってしまいます。
--それで、この client:load
や client:visible
と書くような要素については、それぞれReactやVueとかのライブラリを使って書くということですね。

そうです。Astroとしてはこれらの外部フレームワークなんかのことをIntegrationと呼んでいて、Reactを使いたかったらReactのIntegrationを利用します。プラグインみたいに使えるようになります。
--それはおもしろいですね!

Integrationはどれだけでも導入できるので、たとえばReactのコンポーネントをメインに使いつつ、カルーセルはVueで出来が良いのがあるからそれを使うみたいなこともできます。
--Astroの”フレームワークを混ぜて使える”というところが新しすぎて頭が混乱しています。

実際にそれをやってる人が周りにどれだけいるんだっていう感じですけど、クライアントのフレームワークは何でもいいし、好きなのを使えよっていうスタンスですね。
--じゃあ、さっきの client:visible
みたいな感じで、実際に(クライアントで)必要となった場合、Reactが必要とされている場合はReactも同時にダウンロードされるんでしょうか。

していると思います。
--最初はそこはオーバーヘッドがかかりそうですよね?

React(react-dom)が重いのは、まあ仕方ないというか(笑)
--Reactだけ必要なライブラリだけ別途ダウンロードしておくとか、そういうこともできるんですか?

プリフェッチとかはたぶんできそうなもんですけど、明示的にそれができるかどうかは知らないですね。そもそも必要なときになってはじめてJSをロードする思想なので。
--本当に必要なファイルだけダウンロードしてくるということであれば、もうコンポーネントを構成するファイルとかそれが依存しているファイルのみ手繰って返してくれると…それはそれでなんかすごい気がするけど、それってどうやってるんだろう。簡単なんですかね?

ビルド時にがんばってますね(笑) ビルド時に*.astro
っていうファイルを全部なめて、コンポーネントのclient:*
指定でJSファイルを必要としていることがわかったら、そのコンポーネントをまた全部引いていって、必要なものはチャンクにまとめて…という感じです。
--複数のコンポーネントが同じファイルを参照していたりしたら、どうなるんでしょうか?

その複数のコンポーネントが、共通のチャンクを参照する動きになると思います。そのへんはastroのベースになってる「Vite」というものがあるのですが、Astroのビルドツールは内部的にViteをヘビーに使っていて、これがrollupとかwebpackとかと同じようなことをしてくれるので、ビルドするときに、どこどこの依存関係がどうなっているかを把握して、必要なものはまとめて、分けられるものは分けて…みたいなことをしてくれます。

--Astro自体がこのViteをヘビーに使って凄まじいwebpackみたいな、そういうバンドラーみたいなことをやってくれるんですね。

静的サイトジェネレーター兼バンドラーというイメージですかね。
--なるほど、そういう感じなんだ。じゃあ、事前にかなりビルドしておいて、それをサーバーサイドでSSRしつつ…、でもクライアントロードとかそういうのができるのはDX(デベロッパーエクスペリエンス)がめちゃくちゃ良さそうですね。

そうですね。細かく指定できるので。
--なんとなくアイランドアーキテクチャおよびAstroの動作がつかめてきた気がするのですが、あらためて強調しておきたいところなどはありますか。

何度も言ったとおり、JSが必要なければ必要になるまで何もダウンロードしないので、Astroで実装するだけでページの読み込みスピードは今までより速くなると思います。たとえば先ほどのサンプルコードの VideoPlayer
コンポーネントがどれだけリッチなUIだったとしてもページの初期表示には影響しないです。

--素晴らしい! 最後に、アイランドアーキテクチャの課題って何かありますでしょうか。

何と比較していいかわからないですけど、やっぱりみんなが使ってるようなNextと比べるとまだ歴史が浅いので、「Nextではこういうことできたのになあ…」ということはもちろんあると思います。その反面、「Nextではそうせざるを得なかったけれども、Astroではこうできる」みたいな話もありますけど。
あとは、いまアイランドアーキテクチャを謳ったツールがいっぱい出てきてるので、どれが正解というものがあるわけではないですね。

--すごくたくさんありますね。で、この中で頭ひとつ抜けているのがAstroというわけですね。

ふだんからReactとかVueとか使ってた人からすると、(Astroは)わかりやすくていいなというのはあります。それ以外だと、11tyとかはクラシックなテンプレートエンジンとか、従来の静的サイトジェネレータを使ってきた人たちには書きやすいかもしれないです。ilesはVueにフォーカスしているので、Vue推しの人からするといいかもしれないですね。
--アイランドアーキテクチャ全体の今後の展望みたいなことも含めてお聞きしたいんですが、SPAのフレームワークと棲み分けみたいな感じになってくるんでしょうか。

いままでNextしか選択肢がなかったところに、JSをあまり必要としないケースの選択肢ができたのは良いことだと思います。それですべての人がハッピーかというと、もちろんそうではないし、引き続きJSをたくさん使うのでSPAで、という選択肢も残ると思います。。
今後の展望で言うと、rfcsっていうAstroの次期機能を募るリポジトリがあるので、よかったら参考にしてください。個人的な要望としては、現状のAstroだと、事前に静的サイトジェネレーターみたいに、手元とかCIでビルドして、それをCDNに上げて…というパターンと、Edge WorkerやNodeの実行環境にホストして、SSRだけして返すっていう2パターンの使い方ができるんですけど、択一になるんですよね。「このページは事前に生成したいけど、このページはサーバーから返したい」といったことが今はできないので、こういうところに手が届くようになったらいいなとは思っています。それは今後の課題ですかね。。Astroがメジャーバージョンでリリースされたのがついこの間(Astro 1.0は8/9にリリース)なので、これからかな。勢いはありますね。
--すでに21.1kのGithub Starが付いていますね! 前回のアイランドアーキテクチャのセッションからより詳しく伺えてよかったです。お時間いただいてありがとうございました。

こちらこそありがとうございました。