Server Islands | Astro https://astro.build/blog/future-of-astro-server-islands/
機能が発表されたのはもう半年も前。なんとなーく把握はしてたけど、それをちゃんとしたいという趣旨。
ドキュメントとデモはこちら。
Server islands | Docs https://docs.astro.build/en/guides/server-islands/ Astro Server Islands https://server-islands.com/
TL;DR
.astro
コンポーネントに対して、server:defer
属性をつけておくと- 初回のHTMLレスポンスが返却されるときには、フォールバックコンテンツがまず描画されてる
- その後、クライアント側でJSから本当のコンテンツを遅延でリクエストして、フォールバックを差し替える
いやまぁ、これだけなんよな実際。
server:defer
個人的に盲点だったのは、この指定ができるのは.astro
で書かれたコンポーネントだけってところ。
つまり、ReactやSvelteなんかで書いたものには使えない。
まぁslot = "fallback"
にしても、そらそうかって感じ。
初段のHTMLの様子
output: server
orprerender = false
ならリクエスト時に生成するHTMLoutput: static
orprerender = true
ならビルド時に生成されてるHTML
これはデモサイトでページのソースをみるとわかるけど、こんなの。
<link
rel="preload" as="fetch"
href="/_server-islands/CartCount?e=default&p=E3112300A0AFA5A5171D7A813i6My0q1B4HWDGwK6Xs1gsS4&s=%7B%7D"
crossorigin="anonymous"
>
<script async type="module" data-island-id="4026065b-4200-4403-9a33-d2c99a1a86c0">
let script = document.querySelector('script[data-island-id="4026065b-4200-4403-9a33-d2c99a1a86c0"]');
let response = await fetch('/_server-islands/CartCount?e=default&p=E3112300A0AFA5A5171D7A813i6My0q1B4HWDGwK6Xs1gsS4&s=%7B%7D');
if(response.status === 200 && response.headers.get('content-type') === 'text/html') {
let html = await response.text();
// Swap!
while(script.previousSibling &&
script.previousSibling.nodeType !== 8 &&
script.previousSibling.data !== 'server-island-start') {
script.previousSibling.remove();
}
script.previousSibling?.remove();
let frag = document.createRange().createContextualFragment(html);
script.before(frag);
}
script.remove();
</script>
地道なやつだわな。
裏側の実装は、もはやAstroのお家芸みたいなところであるViteのプラグイン。
さっき見たインラインのJSなんかはここ。
ユースケースを考える
- ログインが必要な情報なんかを表示したいとき
- 動的な内容なので、事前にビルドはできない
- なので、クライアントサイドで
useEffect()
してfetch()
・・・みたいな処理をしてた
このJSコードのペイロードと、処理自体もサーバー側に飛ばせるのは利点かな。
または、
- もともと動的にサーバーでレンダリングしてるけど
- やけに遅いAPIや処理があるとか
Astroの場合、こういうのもStreamingでレスポンスを返すようになってたと思うけど、それでも後続が遅れることに変わりないので、ボトルネックだけをskipできると。
ちなみに、server:defer
なコンポーネントはネストさせることもできるみたいやけど、なんか使い道はあるだろうか。
そのほか
バグか仕様かよくわからなかった挙動もあった。
client:only
したReactなんかのコンポーネントにネストする形で配置すると、リクエストは飛ぶがコンテンツが置き換わらないserver:defer
をつけないものは表示される
Astro Server Island inside Svelte’s
<slot>
inconsistently renders in production · Issue #12394 · withastro/astro https://github.com/withastro/astro/issues/12394
これが近いかな・・?
まとめ
Next.js+RSCほど柔軟ではないけど、まあ堅実な機能って感じかね。
- Astroベースでやることを決めてて
.astro
じゃないコンポーネントは、末端でのみ利用する
という条件下でなら、割と使い所はあるような気はする。
が、大体数のコンポーネントが.astro
ではない場合は、どうだろう。
先述のバグっぽい挙動もあるし、Reactベースなら素直にNext.jsにしたほうが楽そうか。