フンイキプログラマーの僕がAstro.jsをわかりやすいと感じる理由

こんにちは、フンイキプログラマーの自我に目覚めつつある昨今ですが、Astro.jsを触ってみたところ妙に使いやすいしわかりやすいフンイキがあり、一体これはどこから来るのだろうか？と言語化の苦手なフンイキプログラマーが考えてみました。

言い換えれば「他フレームワークとの比較ができればいいけど比較できるほどやりこんでない、でもなんか記事は書きたい」という状況です。

https://astro.build/

今回Astro.jsを使って試しに作ったサイトはこちらです↓

https://timetables-inky.vercel.app/

いろんなサッカーリーグの試合日程APIを読み込んで表示しているだけです。静的に日程を表示しつつ、動的に日程をフィルターする機能もあります。APIリクエスト回数を節約するために、googleスプレッドシートにデータをバッファしてバックエンドにしてます。

## TL;DR（フンイキ説明に我慢できない方用）
- SSRサイトを作るときに
- Astro Islandという仕組みはコンポーネント単位でレンダリング方法を指定できるので
- わかりやすい


## 作りたいのはPV目当てのサイト

この3年くらいVue2とたまにNuxt.jsを使って主に内部システムのフロントエンド開発のお仕事をしています。なので自分プロジェクトを作る時もNuxt.jsでなんとなく（フンイキで）動的なページを作って、でもPV重視でSEO高めたいのでなるべくサーバーサイドレンダリングにできたらいいなあというフンイキで進めます。でしばらくすると大体途中であれ？これSSRになってる？いや？あれ？という感じでわけがわからなくなる、という手法でやらせていただいています。


## Nuxt.jsのわかりにくいフンイキ

https://qiita.com/iz_dot/items/cf62c60c49fe4f5df4f1

Nuxtのレンダリングの設定方法はこちらの記事にも非常にわかりやすく書かれています。書かれているんですが、`ssr`や`target`の組み合わせがたくさんあってこの時点で自分の苦手とするわかりにくいフンイキが漂ってきて大体途中でわけがわからなくなる、という...


## Astro.jsのわかりやすいフンイキ

- デフォルトがSSG（静的サイト生成）
- コンポーネント単位でレンダリング方法を指定できる

Astro.jsではこの「コンポーネント単位でレンダリング方法を指定できる」仕組みをAstro Islandsと呼んでいます。レンダリングのタイミングも条件をつけて指定することができます。

https://docs.astro.build/ja/concepts/islands/

これによって以下のように、コンポーネント単位でレンダリング方法を指定することができ、結果として不要なJavaScriptを可能な限り減らすことでサイトのパフォーマンスを上げることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10505/51c6fb28-91f1-b5fd-2e8e-b3d3a4480d86.png)

自分のサイトはこんな感じになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10505/d11b2ecb-45d2-8a4d-a9b0-83c25214051d.png)

- 赤枠部分のみインタラクティブな機能があるのでクライアントサイドレンダリング
- 他は全部静的HTML

個人的に嬉しいのは「月別」ページが静的HTMLとしてサーバに存在することです。それによってgoogleBot（サイトクローラー）がきちんとコンテンツのあるサイトだと認識しやすい状態になります。googleBotは一応JavaScriptで生成されたコンテンツをクロールできることになっていますが、あまり当てにならないフンイキがあり、PVサイトをやりたいならできるだけ静的HTMLを用意することでgoogleBotの苦手分野に依存することは避けるべきでしょう。


## わかりやすいフンイキの正体！

- コンポーネント単位でレンダリング方法を指定するというやり方は、影響範囲が把握しやすい
- そもそも、自分が求めていたのは「SSRオプションのついているSPA的なフレームワーク」ではなく「クライアントレンダリングオプションのついたMPAフレームワーク」だった。

## その他の好きなポイント

### 好きなフレームワークが使えるので始めやすい

https://docs.astro.build/ja/core-concepts/framework-components/

コンポーネントはいつも使っているような好きなフレームワークを使うことができます。今回は`svelte`を使ってみました。`astro`ファイルにレンダリングオプションを指定しながらコンポーネントを並べていく感じです。

```
├── src
│   ├── components
│   │   ├── Card.svelte
│   │   ├── Footer.svelte
│   │   ├── Header.svelte
│   │   ├── Loading.svelte
│   │   ├── Tables.svelte
│   │   └── TablesFiltered.svelte
│   ├── layouts
│   │   └── Layout.astro
├── pages
│   ├── [league]
│   │   └── [season]
│   │       ├── [month].astro
│   │       └── index.astro
│   └── index.astro
```

### 速い（静的HTMLが）

体感ですが、上記の月別ページの表示とかサクサクです（画像は別サーバから呼んでるので遅いです）。

https://timetables-inky.vercel.app/epl/2023/2023-08


### とりあえずのデプロイが簡単

https://docs.astro.build/ja/guides/deploy/

今回vercelを使いました。`npm install -g vercel` して `vercel` しただけで設定完了したような。


### meta、ogpを入れるのが簡単

これがベストプラクティスなのかは不明ではありますが、各ページコンポーネントからメインのレイアウトのastroファイルに、個別の情報を渡す

```vue
<Layout siteName={title} description={text}>
  ....
</Layout>
```
必要であればレイアウトファイル側で修正などする。で、あとは普通にheadタグ内に入れていく。

```html
<head>
    <title>{meta.siteName}</title>
    <meta name="description" content={meta.description} />
    <meta property="og:url" content={ogp.url} />
    <meta property="og:type" content={ogp.type} />
    <meta property="og:title" content={ogp.title} />
....
</head>
```
↓ 設定したOGPタイトル、画像がきちんと表示されています。画像はfavicon生成サイトにあったフリー素材のクジラです。

https://timetables-inky.vercel.app/jleague-1/2023


html的だとわかりやすいフンイキを感じる派です。nuxtみたいに急にconfigファイルが登場してこないのもよい。

## 気になっている点

### ビルド時間をもっと短くしたい

今回の作りでは、各サッカーリーグのシーズンごとのページにそのシーズンの月ごとのページを生成しています。月ページのルーティングを作るために`[month].astro`内の`getStaticPaths`からAPIリクエストしてルートデータと表示データを作るのですが、ローカル環境で月別ページにアクセスするとその時点でページを作るので表示に20秒くらいかかります。

そもそもルーティングを取得する処理はAstroから出して、ルートと表示用データをjsonにまとめる別処理を作った方がいい気がしてきてます。いずれにせよ最新データ取得して定期的にビルドする必要もありますね。

その辺、よいアプローチありましたら誰か教えてください。

## まとめ

- 久しぶりに記事が書けてよかった！