Astro × microCMSで実現する静的サイト制作

こんにちは!お久しぶりです。

フロントエンドエンジニア

ガッシュ

 

本記事はAstroやmicroCMSに興味がある方に向けて、Astroの利点やmicroCMSの活用方法を一部ご紹介していきます。

Astroについて

まず前提として今回作成依頼を受けたのが、静的サイトとなります。

Astroは基本的にSSG(Static Site Generation)で構築され、ビルド時にサーバー側で生成されたHTMLだけをクライアントに返します。

つまり、無駄なJavaScriptをクライアントで読み込む必要がなく、ビュアーの環境に依存しない軽量なサイトを作成することができます。

また、AstroはMPA(マルチページアプリケーション)を採用しているため、ページごとに独立した HTML が生成されます。

この仕組みにより、ページ間の依存関係が少なく、SEOに強く、個々のページがシンプルで高速に表示されるという利点があります。

Next.jsのSSGとの比較

Next.jsでもSSGは可能です。 構造自体は似ていますが、Next.jsはReactベースのためハイドレーションが発生します。

Reactは仮想DOMを構築し、差分が生じたときに必要な部分だけDOMを更新する仕組みを持つため、SSGで生成されたHTMLであっても、クライアント側で再度JavaScriptを実行して仮想DOMを構築する必要があります。
➡このハイドレーションにより、多少なりとも無駄なJSがロードされてしまう。

これはインタラクティブな要素が絡む動的サイトや複雑な状態管理を必要とするアプリでは真価を発揮しますが、今回のような静的サイトではあまり利点がありません。

またその一方Astroでも、必要な部分だけReactなどのコンポーネントを組み込んで、クライアント側でインタラクティブに動作させることも可能です。

フレームワークは異なれど、 それぞれMPAの軽量さとSPAのインタラクティブ性を兼ね備えた設計になっていますね。

開発者がコンポーネント単位で最適なレンダリング形式を選択することで、静的な部分は軽量で高速に、インタラクティブな部分は動的に、といったメリハリのある設計が可能にしてくれます。

microCMS

CMSはmicroCMSを採用しました。

microCMSはヘッドレスCMSであるため、コンテンツはAPI経由で取得します。

従来型CMSのようにフロントエンドと密結合していないため、使用するデザインやフレームワークに制約されず、自由度の高い開発が可能です。

AstroのSSGと組み合わせることで、取得したコンテンツをビルド時に静的HTMLとして出力できますし、またWebhookを利用することで、コンテンツ編集→公開後に自動的に再ビルドするように制御することもできます。

そのため、WordPressのようにユーザーのリクエスト時にサーバー側で動的にHTMLを生成する動的MPAではなく、ビルド時にHTMLを生成する静的MPA(SSG + MPA)となります。

フローとしては以下の通りです。

[MicroCMS 管理画面]
コンテンツ作成・編集
 │

[MicroCMS API] ── GET /contents ──➡ [Astro サイトのビルド時]
                        │
                       ▼
                        [Astro 静的ページ生成]
                        │ 
                       ▼
                         [ブラウザに表示]

また、後ほど紹介するプレビュー機能にも対応しているため、公開前に画面上で確認することも可能です。

開発例

今回は、シンプルなAPIの実装方法プレビュー機能について紹介します。

※本稿ではAstroのセットアップやmicroCMS SDKの導入手順は割愛します。

例として、「タイトル」と「内容」を持つお知らせコンテンツを想定して進めていきます。

コンテンツを取得&表示

まずはコンテンツを取得する汎用な関数を用意します。

microcms.ts

import type { MicroCMSQueries, MicroCMSListContent, MicroCMSObjectContent } from 'microcms-js-sdk';
import { createClient } from 'microcms-js-sdk';

const client = createClient({
  serviceDomain: import.meta.env.MICROCMS_SERVICE_DOMAIN,
  apiKey: import.meta.env.MICROCMS_API_KEY,
});

type MicroCmsRequest = { endpoint: string; queries?: MicroCMSQueries };

export const getMicroCmsList = async ({ endpoint, queries }: MicroCmsRequest) => {
    const response = await client.getList({ endpoint, queries });
    return response;
};
 

呼び出し元のastroファイルを記載します。

pages/index.astro

---
import Layout from '~/layouts/Layout.astro';
import { getMicroCmsList } from '~/lib/microcms';

type NewsContent = {
  title: string;
  detail: string;
};

const newsList = await getMicroCmsList({
  endpoint: 'news',
  queries: { fields: ['title', 'detail'] },
});
---
<Layout>
 <h1>記事一覧</h1>
 {newsList.contents.map((content)=>(
  <h2>{content.title}</h2>
  <p>{content.detail}</p>
 )) }
</Layout>

開発環境を立ち上げると無事表示されることを確認できました。

画面プレビュー機能

次に、画面プレビューを実装していきます。

microCMSの管理画面から、画面プレビューボタン押下時の遷移先URLを指定することで、

コンテンツ公開前に、下書き状態のコンテンツが反映されたサイトをプレビューすることが可能になります。

今回は開発環境で検証するので、以下のようにします。

説明にも記載されてますが、画面プレビュー押下時には「draftKey」がパラメータキーとしてURLに付与されます。

実装ではそのdraftKeyをクエリに仕込むことで、下書き状態のコンテンツを取得できます。

※詳しくは公式ドキュメントをご覧ください。

pages/index.astro

---
import Layout from '~/layouts/Layout.astro';
import { getMicroCmsList } from '~/lib/microcms';

type NewsContent = {
  title: string;
  detail: string;
};

// URL から draftKey を取得
const url = new URL(Astro.url.href);
const draftKey = url.searchParams.get('draftKey') ?? undefined;

const newsList = await getMicroCmsList({
  endpoint: 'news',
  queries: { fields: ['title', 'detail'], draftKey: draftKey },
});
---

<Layout>
 <h1>記事一覧</h1>
 {newsList.contents.map((content)=>(
  <h2>{content.title}</h2>
  <p>{content.detail}</p>
 )) }
</Layout>

これでコンテンツを下書き状態にすれば、画面プレビューから下書きの状態で確認できます。

(・・・しかし、なぜか下書き状態が反映されない?!)

 

調査したところ、draftKeyが空で返ってきてるようです。

(URLを確認する限り、draftKeyは設定されてるはずですが・・・)

気づき

先ほども述べましたがAstroはデフォルトではSSG(静的サイト生成)で動作します。

ビルド時にページのHTMLをあらかじめ作成するため、ユーザーがアクセスするURLに依存する情報(例えばmicroCMSのdraftKey)を取得することができません。

その結果、SSGのままではdraftKeyが常に空になり、下書きコンテンツを表示できません。

よってSSGを無効化します。

pages/index.astro

---
import Layout from '~/layouts/Layout.astro';
import { getMicroCmsList } from '~/lib/microcms';

// SSGの無効化
export const prerender = false;

type NewsContent = {
  title: string;
  detail: string;
};

const url = new URL(Astro.url.href);
const draftKey = url.searchParams.get('draftKey') ?? undefined;

const newsList = await getMicroCmsList({
  endpoint: 'news',
  queries: { fields: ['title', 'detail'], draftKey: draftKey },
});
---

<Layout>
 <h1>記事一覧</h1>
 {newsList.contents.map((content)=>(
  <h2>{content.title}</h2>
  <p>{content.detail}</p>
 )) }
</Layout>

こうすると、ページがSSGではなくSSRとして動作する(リクエストごとに生成される)ため、アクセス時のURLに含まれるをdraftKeyを正しく取得できるようになります。

ただこの状態だと本番でも下書き状態が表示されて画面プレビュー機能として成り立たないので、コンポーネントとして共通部分を切り出し、プレビュー用のページを作成します。

components/NewsTemplate.astro

---
import Layout from '~/layouts/Layout.astro';
import { getMicroCmsList } from '~/lib/microcms';

type Props = {
 draftkey? : string
}

type NewsContent = {
  title: string;
  detail: string;
};

const url = new URL(Astro.url.href);
const draftKey = url.searchParams.get('draftKey') ?? undefined;

const newsList = await getMicroCmsList({
  endpoint: 'news',
  queries: { fields: ['title', 'detail'], draftKey: draftKey },
});
---

<Layout>
 <h1>記事一覧</h1>
 {newsList.contents.map((content)=>(
  <h2>{content.title}</h2>
  <p>{content.detail}</p>
 )) }
</Layout>


pages/preview/index.astro(プレビュー用)

---
import HomeTemplate from '~/components/NewsTemplate.astro';

export const prerender = false;

// URL から draftKey を取得
const url = new URL(Astro.url.href);
const draftKey = url.searchParams.get('draftKey') ?? undefined;
---

<NewsTemplate draftKey={draftKey} />


pages/index.astro(本番用)

---
import  NewsTemplate from '~/components/NewsTemplate.astro';

export const prerender = true;
---

<NewsTemplate />

最後に画面プレビューURLのパスを変更すれば完了です。

まとめ

今回はAstroとmicroCMSを活用して、画面プレビュー機能を実装してみました。

画面プレビュー機能については他にも、公開用と下書き用でそれぞれ専用のAPIキーを発行する方法もありますが、それらは有料プランでしたので、無料でできる方法を紹介しました。

レンダリング形式の違いについても勉強になるきっかけとなったので、気になる方はぜひ活用してみてください。

最後に

ロジカルスタジオでは、エンジニアを募集中です!

ぜひ下のバナーから確認してみて下さい!