🚀

Astroを使ってブログサイトを作成する

2024/06/19に公開

モチベーション

ブログサイトを作りました。
いままでは Qiita に技術記事を投稿していましたが、 投稿プラットフォームを Zenn に切り替えるにあたり、今まで書いた記事をそのまま Zenn に流用せずに、自分のブログサイトを作って管理しようと思い立ち、開発を開始しました。

技術スタック

フレームワーク

フレームワークの選定にあたって考慮したことは、 いままでに書いた記事の Markdown ファイルを使えることでした。
次に、今まで触ったことのないフレームワークを使ってみたいと思っており、それでいてあまり時間をかけずに済むように学習コストが低いものが好ましいと思い、書き慣れている React を使って書けるフレームワークを探しました。
素の React でも react-markdown などのライブラリを使えば Markdown ファイルを表示することはできますが、 CSR では SEO 的に弱い為、 SEO に強く、パフォーマンスも良いとされる SSG の機能を持つ何かしらのフレームワークを使う必要がありました。

SSG の機能がある React ベースのフレームワークで調べたところ、 Next.js, vike(vite-plugin-ssr) などの候補があがりましたが、 以下のような理由で Astro を選びました。

  • 内部で vite を使っており、開発サーバーの立ち上がりと build が高速なので快適に開発できる
  • テンプレートが JSX に似た書き味
  • React を使って書くことも可能
  • web ページの描画が高速
  • Markdown ファイルとの相性が良い

https://docs.astro.build

Astroは、ブログやマーケティング、eコマースなど、コンテンツ駆動のウェブサイトを作成するためのウェブフレームワークです。Astroは、新しいフロントエンドアーキテクチャを開拓し、他のフレームワークと比較してJavaScriptのオーバーヘッドと複雑さを低減することで知られています。高速でSEOに優れたウェブサイトが必要なら、Astroが最適です。

設計

アプリの基盤として Astro を使用して、主に ルーティング, ページの表示, 共通のheadタグ を担わせました。
Astro ではファイルベースルーティングを採用しており、 pages ディレクトリに配置した.astroファイルはそのままページとして表示されます。なので pages ディレクトリ配下の .astro ファイルには最低限の HTML を書き、 UIコンポーネント は原則 React で書き、 .astro ファイルから呼び出すことでレンダリングします。

layouts ディレクトリにヘッダーやナビゲーションバー、フッターなど、ページ間で共有される共通の UI 及び、UI のスタイリングと、共通の head タグを提供する Astro コンポーネントを配置しました。
ヘッダーやフッターなど UI に関係する箇所とそれぞれのスタイルは React コンポーネントに切り出して実装します。

これにより、 Astro 特有の構文に悩むことなく、普段書いている通りに UI コンポーネントの開発ができるので、開発コストを減らせました。

UIライブラリ

UI ライブラリには Radix UI を選びました。

https://www.radix-ui.com/

Radix UI は componentベース で提供されるヘッドレス UI であり、ヘッドレス UI はスタイル等の見た目を提供しない UI ライブラリです。

Radix UI を選んだ理由としては、 提供する Box コンポーネントや Flex コンポーネントを組み合わせてレイアウトを作るのですが、 それぞれ paddingmarginheightwidth を props として渡すことができ、また、 Flex コンポーネントでは direction, align, justify を設定することができる為、簡単にレイアウトを整えることができます。

Markdownファイルの表示

Astro のコンテンツコレクションという仕組みを使うことで簡単に Markdown ファイルをブラウザに表示することができます。

src/content ディレクトリの中に blog ディレクトリを作り、 blog ディレクトリの中にブログコンテンツを置くことで、 zod を用いて blog ディレクトリの中の Markdown ファイルのフロントマターに型を付けることができ、 .astroファイルから Astro.getCollecition("blog") 関数を使うことで src/content/blog ディレクトリにあるコンテンツを全て取得することができます。

さらに URL をもとに、取得したコンテンツの中の1つを選択して画面に表示することもできます。

[id].astro
---
export async function getStaticPaths() {
  const blogEntries = await Astro.getCollecition("blog");

  return blogEntries.map((entry) => ({
    params: { id: entry.id },
    props: { entry },
  }));
}
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<Layout>
  <Content />
</Layout>

markdown parser に関しては、今後 zenn に書いた記事を個人ブログサイトへ移行することも考えて、同じ記法で書けるようにと zenn-markdown を使いました。

https://zenn.dev/rorisutarou/articles/ec3871ec55693d

デプロイ

Cloudflare Pages を使いました。
GitHub のレポジトリと連携するだけで PR の作成時にはプレビュー環境を、 main に merge した際には production 環境へのデプロイを自動でしてくれます。

CI/CD

PR 作成時には プレビュー環境へのデプロイと main へと merge した時には production 環境へのデプロイを自動でして欲しくはありましたが、それぞれ lint, test, Astro の check コマンドによる CI のうちどれかが失敗したらデプロイしないようにしたかったので、 Cloudflare 側の自動デプロイはオフにして、 GitHub Asctions でそれぞれデプロイするようにしました。

Cloudflare のコンソールから CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN を取得し、 GitHub リポジトリの Actions secrets and variables に設定します。

merge.yml
name: Deploy to Cloudflare Pages on merge
on:
  push:
    branches:
      - main
jobs:
  deploy:
    permissions:
      contents: read
      deployments: write
    runs-on: ubuntu-latest
    steps:
      // 一部省略
      - name: Publish
        uses: cloudflare/pages-action@v1
        with:
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          projectName: blog
          directory: ./dist
          gitHubToken: ${{ secrets.GITHUB_TOKEN }}

https://zenn.dev/monicle/articles/4613eb4064dc40

まとめ

Astro を使って簡単に Markdown を表示できるブログサイトの開発をすることができました。
フロントエンドは SPA であるべきといった固定概念にとらわれていた節がありましたが、 Astro は MPA であり、しかも速いという設計です。
また、 Astro アイランドというアーキテクチャや、 React だけじゃなく vue や svelte も同じプロジェクトで使えて、面白いと感じるとともに、よりフレキシブルにいろんな開発者がそれぞれの強みを活かして一つのプロジェクトに携わることができるようになるだろうと思いました。
基本的な HTML, CSS を知っていれば web サイトを作ることができ、フロントエンドエンジニア以外でも触ることができるので、もっと Astro が広がることでしょう。

Discussion