鳥小屋.txt

主に自作ゲームをつくったりしているよ。制作に関することやそうじゃないことのごった煮ブログ

自分のサイトをAstroで作り直した話

定期的にやるやつ!

背景・モチベーション

(※個人的な話なので興味ない人は飛ばそう)

いろいろ使いたい

個人的な思想として、個人でやっているWebサイトはそれぞれ違う技術を使いたいというモチベーションがあります。

どうしてもお仕事だと長期的な保守のことやチーム開発のことを考えると、あまり尖った道具を採用することはありません。正直、よほど強い理由がない限りは React 系一択になる。

そのため、個人でやってるプロダクトでは、あまりお仕事で使ってないやつを試してみたい!というモチベーションがあり、特定の何かだけを使うのではなく、サイトごとに全部違うものを使ってみたいと思っています。

あと、僕個人のマインドとしてはテンプレート系が好きだったりはします。テンプレートが好きというかHTMLとCSSが好き。

そのため、現在も僕が保守している3つのサイトは以下のような技術構成になっていました。

  1. 鳥小屋ポータRu
    • 僕の個人サークルのWebサイト
    • Sveltekit
  2. 鳥小屋プラグイン置き場
    • RPGツクール用のプラグインの公開サイト
    • Astro.js
  3. プレポタ
    • ブラウザゲームの遊べるサイト
    • Sveltekit

……あれ、Sveltekit が2つになっちゃってるじゃん!!!111 ←モチベーション

それぞれのサイトに適したものを考える

以前の記事にもちらっと書いていましたが、『3. プレポタ』のサイトを作る際、最初は HonoX を使おうと考えていました。が、サイトの性質上、SinglePageApplicationのページであったほうが都合が良いという理由があり、おそらくプレポタの要件にあっているのは Sveltekit だろうと考え Sveltekit を採用しています。

『1. 鳥小屋ポータRu』のサイトは動的な要素はそんなに多くないため、正直 Sveltekit はオーバースペックです。別に Sveltekit である必要はない気がします。

そこで、それぞれのサイトに求められる要件をまとめ、採用技術の整理をすることを考えました。

  1. 鳥小屋ポータRu(個人サークルのページ)
    • =作ったゲームたちの公式サイトの集合体
    • 各ゲームごとにページのデザインは違う場合がある
      • ゲームの雰囲気にあわせてページの構成が変わったりする
      • 画像が多いのでちゃんと最適化とか必要
    • 画像最適化などの公式サポートがある Astro がよさそう
  2. 鳥小屋プラグイン置き場(RPGツクールのプラグイン公開サイト)
    • すべてのページがデザイン固定
    • コンテンツはすべて Markdown で書いている
    • ページ数も少なめ。画像とかも少ない。
    • ぶっちゃけなんでも良さそう!
  3. プレポタ(ブラウザゲームのサイト)
    • ログイン・ゲームのスコアランキングなどの動的要素があり、ステート管理やJavaScriptの処理が多い
      • SinglePageApplication 的な要求が多い
    • 引き続き Sveltekit を使うのが良さそう!

というわけで、『1. 鳥小屋ポータRu』については Sveltekit ではなく Astro に置き換えることにしました。Astro は既に『2. 鳥小屋プラグイン置き場』で使ってしまっていますが、こちらのサイトは別に Astro である必要もそこまでないので、別の機会に何か面白そうなものに置き換えることにします。

本題:Sveltekit → Astro の置き換え

置き換え、といいつつ、Astro の中ではコンポーネントとして Svelte を利用することができます。そのため、元のサイトで使用していた一部のコンポーネントは概ねそのまま持っていくことができました。

……が、Astro の特徴の1つとして Astroアイランド があります。

Astro は基本的には静的サイトとしてビルドすると、ページ内から JavaScript が消え去ります。JavaScript で動く動的な要素を利用したい場合は、その部分だけをアイランドとして作成する必要があります。

特に問題ないでしょ~と思ったのですが、実際に移植している中で以下のような問題がありました。

.astro のコンポーネント以外から .astro のコンポーネントは呼べない

それはそう。

例えば、 Svelte のコンポーネントの中で以下のようなことはできません。

<script>
import MyAstroComponent from './MyAstroComponent.astro'; // ←だめ
</script>

<MyAstroComponent  />

例えば、サイトのロゴとかアイコンのような 共通部品を .astro のコンポーネントで作っていると、 Svelte などの他のフレームワークの中からは参照できなくなってしまいます

これを避けるためには『基本的に全部 Svelte など他のフレームワークを使う』または『呼び出し元から slot / children として渡す』必要があります。

---
// 呼び出し元で、Astro のコンポーネントを import し、 slot / children で渡す
import MySvelteComponent from './MySvelteComponent.svelte';
import MyAstroComponent from './MyAstroComponent .astro';
---

<MySvelteComponent>
  <MyAstroComponent />
</MySvelteComponent>

何もかも Svelte で実装するのもどうなんだ?という点と、 astro:assets の提供する画像最適化を行う <Image> コンポーネントを使いたかったので、僕は呼び出し元から渡す方法で実装を行いました

が、一部で次の問題にぶち当たります。

Astroアイランドを使うと <astro-island> という要素が生成される

例えば以下のような Astro ファイルを作成します。

---
import MySvelteComponent from './MySvelteComponent.svelte';
---

<div class="hoge">
  <p>こんにちは</p>
  <MySvelteComponent  />
</div>

するとレンダリング後のHTMLは以下のようになります。

<div class="hoge">
  <p>こんにちは</p>
  <astro-island uid="xxxx" ....>
    <!-- ここに MySvelteComponent  の中身 -->
  </astro-island>
</div>

<astro-island> という要素が作成され、その中にコンポーネントは描画されます。ハイドレーションするための情報がここに渡されているためです。

が、これによってHTMLの構造が変わることが非常に困った。

具体的に何に困ったのかというと『カルーセル』です。

ゲームの紹介ページなので、スクリーンショットを載せるようにカルーセルを多用していました。カルーセルの中には当然画像が入るため、画像については astro:assets<Image> コンポーネントを使いたく、先ほどの呼び出し元から .astro のコンポーネントを渡したくなります。

// イメージ
<CarouselComponent>
  <div class="item"><Image src={image01} /></div>
  <div class="item"><Image src={image02} /></div>
  <div class="item"><Image src={image03} /></div>
</CarouselComponent>

が、カルーセルのUIライブラリは結構HTMLの構造に依存するものが多く、間によくわからないHTMLが挟まると動かない場合がありました><;

ひとまず今回いろいろ試したところ Embla Carousel は class 名などがなくても動かすことができたため、画像のカルーセルについては Embla Carousel を使う形で乗り切りました

今思えば、画像だけのカルーセルの場合は、 Astro の getImage 関数で事前に最適化した画像のURLを作成して、その値を Svelte のコンポーネントに渡して Svelte の世界だけで完結させるという方法でも良かったかもしれません。

言い訳をすると、元のサイトには画像だけじゃないHTML要素もいろいろあるカルーセルもあり、こういった方法を取っていました。が、そっちは別のCSS的な問題でカルーセル周りが思うように動かない問題があり、最終的に画像以外のカルーセルは無くすという対応をしました><

おまけ:グローバルスタイルどこに置くの問題

Astro 、グローバルのCSSをどこに置いたらいいんでしょうね……

公式的には Layout の先頭で import しようね、という感じなのでしょうが、エントリーポイントでの import の順番で CSS 読み込まれる順番が変わる可能性があるの、ちょっとやな感じがします。

とりあえず今回は「先頭で読まなきゃだめだよ!」みたいなコメント書いて未来の僕に託しましたが、いつか壊しそ~。

@layer の優先度設定するやつだけは何よりも先頭に読みこませたいので、本当はグローバルに必ず一番最初に読み込まれるCSSを設定したいなぁ。どうやるのがいいんだろうなぁ。

できた!

ページ数がまぁまぁあるので少し時間はかかりましたが、全ページきっちり移植完了しました。

Astro はビルド時にJavaScriptを消してくれたり画像の最適化もしてくれたりするので、なんとなく体感も早くなった気がします。試しに PageSpeed Insights してみたところ、モバイルでもパフォーマンス100点出せる場合があり、ほえーという気持ちに。

でもTOPページに動画流れるページのパフォーマンスが100点なわけないでしょ

また、今回の Astro 化によってサイト自体がSPAからMPAに戻ったわけですが、このサイトはページごとにデザイン(というか配色)が違う場合もあり、SPA遷移してもキレイではなかったので、そういった意味でもMPAのほうが向いてるサイトだったかもしれませんね。


さて、次は Astro が2つになっちゃったので、『2. 鳥小屋プラグイン置き場』を別のなにかに置き換える旅ですね!こっちはページ自体シンプルだし、以前試してやめちゃった HonoX でもいいし、なんか全然違うもの使ってみても良さそう。なんか面白そうなのあるかなー?