爆速サイトを構築する、静的サイトビルダー「Astro」の紹介

# はじめに
先日[こちら](https://increments.connpass.com/event/261907/)のイベントに出させてもらった際、`Astro`を知ることになり、使ってみたいなーと思っていたところ会社の新サービスLPで利用できる機会がありました。

https://increments.connpass.com/event/261907/

社内のフロントエンドチームの中でも利用した事例はなかったですが、
- 社内で一番利用されている、React(JSX)ベースでも書くことができる
- Astro形式のファイルもSPAフレームワーク利用者なら問題なくキャッチアップできそう

ということもあり、Astroを選定することができました。

まだ**Astroについて知らない人**や**利用を検討している人**へ、本記事での「**実際に`Astro`利用した感想**」や、「**`Next.js`とのパフォーマンス比較**」をもとに導入のきっかけとなれば幸いです。

## こんな人に読んで欲しい
- Astroってそもそも何？って人
- Astroの利用を検討している人
- AstroとNext.jsの比較を知りたい人

# Astroについて
そもそも`Astro`について知らない人も多いかと思います。
`Astro`とは可能な限りJavaScriptを取り除き、最適化された静的HTMLを作成するために作られた静的サイトビルダーです。(`Gatsby.js`のようなイメージです。)
動的なコンテンツを利用しない限りクライアントのJSの読み込みが「**0KB**」となり、サイトのビルド時には**HTMLとCSSのみ**が出力されます。
そのためページ遷移時は**SPA**のように[History API](https://developer.mozilla.org/ja/docs/Web/API/History_API)を利用せず、昔ながらのページの再読み込みを行う**MPA**形式となります。

[**参考**]

https://astro.build/

https://www.azusuki.com/spa-mpa/


# なんでAstroを利用するのか？
SPAフレームワークでもSSR/SSG/ISRなど、様々な方法で描画を最適化しようという動きはあります。
その中でなぜ`Astro`を利用するのかというと、大きくは以下が理由になるかと思います。

- 流行りのSPAフレームワークを利用すると、**JSファイルのサイズが重い**
- JSを読み込んだ後、**ハイドレーションの処理に時間がかかる**

そもそもSPAフレームワークが生まれた背景として、「画面遷移の際に読み込むデータを最適化する」、「動的コンテンツをブラウザで扱いやすくする」という目的があり、**静的サイトをビルドするために作られたフレームワークでない**ということから、上記のようなギャップが生まれています。

このことから**静的サイトの最適化**という観点で、Astroのような静的サイトビルダーや/フレームワークがフロントエンド界隈で台頭してきています。

[**参考**]

https://nextjs-ja-translation-docs.vercel.app/docs/basic-features/pages#%E3%83%97%E3%83%AA%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0

https://docs.astro.build/ja/concepts/why-astro/

# Astroを利用してみて

`Astro`を利用してLPを構築しましたが、以下が実装面のメリットとして大きいように感じました。

- `Vue`や`React`のようなUIフレームワークに加え、`MD/MDX`形式での実装が可能なので学習/運用コストを低くすることができる
- コンポーネント思考で画面設計が可能
- ファイルベースのルーティング
- 動的なコンポーネントを好きなタイミングでレンダリングすることが可能（**Astro Islands**）

社内で利用しているメインフレームワークが`React`のため、今回は`.astro`と`.tsx`形式でコンポーネントを作成しました。
`Next.js`のように`/pages`配下から`/components`を呼び出すように実装でき、`Next.js`ユーザーにはとても直感的でした。

例えばこんな感じのコンポーネントを作成したら、
```/conponents/Counter.tsx
import { useState } from "react";

export const Counter = () => {
    const [count, setCount] = useState(0);

    return (
        <button onClick={() => setCount(count + 1)}>
            {`Count: ${count}`}
        </button>
    );
};
```

以下のような形式で呼び出すことができます。(`Astro`まだハイライトされてない；；)
```/pages/index.astro
---
import { Counter } from "../components/Counter";

const title = "Astro Example";
---
<main>
  <h1>{title}</h1>
  <Counter client:load />
</main>

```

この中にある、
```html
<Counter client:load />
```
ですが、これが`Astro`の特徴である「**Astro Islands**」になります。
静的なページを作成する際も、SPAのような動的なコンテンツを利用したいタイミングがあるかと思います。
その場合は`client`というディレクティブを必ずつける必要があり、これを付与することで「**対象のコンポーネントのみ**」をターゲットとしたハイドレーションを実行します。
また`client`は実行タイミングを選択することができ、「**スクロール地点**」や「**画面サイズ**」などの細かい設定も可能です。
[こちら](https://docs.astro.build/en/reference/directives-reference/#clientload)を利用することにより、「最初に呼び出したいコンポーネント」や「後からの描画でも問題ないコンポーネント」などの重みづけを簡単に設定することができます。

[**参考**]

https://docs.astro.build/en/guides/integrations-guide/

https://docs.astro.build/en/guides/markdown-content/

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

https://docs.astro.build/en/reference/directives-reference/#clientload

# Next.jsとAstroを比較してみた
今回の検証ではベースとなる`Next.js/Astro`に`Tailwind CSS`を追加したのみの構成となっています。

```sh
# Next.js Project
yarn create next-app --typescript
# https://tailwindcss.com/docs/guides/nextjs
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

# Astro project
yarn create astro
# https://docs.astro.build/en/guides/integrations-guide/tailwind/
yarn astro add tailwind
```

`Astro`でLPサイトを作成し、`Next.js`の形式に移植をします。
`Astro`的な書き方の箇所をJSX形式に変換するだけでしたので、15~20分ほどで作業は完了しました。
(JSX形式で実装していたコンポーネントは、そのまま流用することができました。)

各環境で静的ファイルをExportし、`http-server`を用いてローカルサーバを立てます。
これで各環境での検証準備が整いました。

```sh
# 静的ファイルを出力したディレクトリへ移動
# デフォルトだと Next.jsは 「/out」、 Astroは 「/dist」 に出力されます。
$ cd /staticfFile/path

# port 8080でサーバーが起動
$ npx http-server
Starting up http-server, serving ./
...
Available on:
  http://127.0.0.1:8080
  http://192.168.3.7:8080
Hit CTRL-C to stop the server
```

## Lighthouse
今回は`localhost`の環境で、Chromeのdevtoolにある「Lighthouse」を利用しました。

結果を見てみると「Largest Contentful Paint」の項目が「**約2.5倍**」も違います。
こちらの項目は、初期ロードから最も大きい要素がレンダリングされるまでの時間になります。
Next.js/Astro共に最適化をしていませんが、デフォルトではかなり大きな差分を生んでいるように見えます。

[左が**Next.js**、右が**Astro**]
![lighthouse.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469819/c8f018f5-e4be-c67f-1279-bbb578d0e493.png)


## リクエストサイズ
「Document」や「Stylesheet」のリソースサイズも違いますが、一番大きな差分は「**Script**」ということがわかります。
冒頭でも説明しましたが、`Astro`は**可能な限りJavaScriptを読み込まない**ためこれだけ大きな差分を生んでいます。
Next.jsは静的な画面1ページの描画でも「**246.2KB**」ものScriptを読み込む必要があるのですね。

[左が**Next.js**、右が**Astro**]
![Group 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469819/e7561333-529f-eb7c-d711-141b4df1eef5.png)



# 最後に
各環境で最適化を行えば結果が変わるかもしれませんが、私の検証環境ではこのような結果となりました。
`Astro`を 使ってみたい/検討している という人の参考になれば幸いです！
また本記事では`Astro`と`Next.js`の**静的サイト**の比較を行いましたが、**ハイドレーションを多用した動的サイト**での検証はできておりません。
`Astro`は`React`以外にも`Vue`,`Svelte`など様々なフロントエンドフレームワークを実行することができるので、既存のアプリケーションでA/Bテストを行った方がいましたらぜひ教えていただきたいです！