🚀
ブログサイト構築 — Astro SSG と MPA 設計
Astro の静的サイト生成(SSG)・MPA・アイランドアーキテクチャの概念整理。SPAとの違いと本サイトでの採用判断
SPA / MPA / SSG の整理
SPA(Single Page Application)
クライアントが最初に空の HTML + JS バンドルを受け取り、以後のナビゲーションはすべて JS がルーティングを担う。
初回: server → index.html(ほぼ空)+ app.js(巨大)
以後: JS がルートを解析して仮想DOMを更新
利点: ページ遷移が高速・リッチなインタラクション
欠点: 初回 JS 読み込みが重い・SEO が難しい・クローラーへの追加対応が必要
MPA(Multi Page Application)
ページ遷移のたびにサーバーが完全な HTML を返す従来型の構成。
/blog → server → blog.html(完全な HTML)
/docs → server → docs.html(完全な HTML)
利点: SEO が強い・初期表示が速い・JS 障害耐性
欠点: ページ遷移のたびに全 HTML を再レンダリング
SSG(Static Site Generation)
ビルド時にすべてのページの HTML を生成し、CDN で配信する。
build time: Markdown + テンプレート → HTML(全ページ分)
runtime: CDN がそのまま返す(サーバー処理なし)
利点: 高速(CDN キャッシュ)・安価・スケーラブル
欠点: 動的コンテンツ(リアルタイムデータ)が難しい
Astro の採用
本サイトは SSG ベースの MPA として Astro を採用。
pnpm build
→ src/pages/**/*.astro + src/content/**/*.md
→ dist/**/*.html(全記事ぶんの静的 HTML)
→ dist/ogp/**/*.png(全記事ぶんの OGP 画像)
npx wrangler pages deploy dist
→ Cloudflare Pages CDN に配信
なぜ SPA にしなかったか
ブログ・Docs サイトの主な用途は「検索エンジンからの流入」と「コンテンツの閲覧」。
SEO と初期表示速度が最優先であり、SPA のインタラクティビティは不要。
アイランドアーキテクチャ
Astro はデフォルトで JS を生成しない。
一部の UI だけをインタラクティブにしたいとき、Island として client:* ディレクティブを指定する。
<!-- サーバーサイド(ビルド時)レンダリング: JS なし -->
<StaticComponent />
<!-- クライアントサイド hydration: ページロード後に JS が有効化 -->
<InteractiveCounter client:load />
本サイトでのアイランド:
| コンポーネント | ディレクティブ | 理由 |
|---|---|---|
| ViewCounter | インラインスクリプト | fetch + localStorage |
| SpeechButton | インラインスクリプト | Web Speech API |
| テーマ切替 | インラインスクリプト | CSS 変数の動的変更 |
| 検索 (Pagefind) | <script> タグ | Pagefind JS ライブラリ |
Content Collections
Astro の Content Collections は src/content/ のファイルを型安全に扱う仕組み。
// src/content.config.ts
const blog = defineCollection({
loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
schema: z.object({
title: z.string(),
description: z.string().optional(),
date: z.date(),
tags: z.array(z.string()).default([]),
image: z.string().optional(),
series: z.array(z.string()).optional(),
}),
});
ページでの利用:
// src/pages/blog/[...slug].astro
const posts = await getCollection('blog');
// posts は CollectionEntry<'blog'>[] 型で型チェック済み
この設計の利点:
- frontmatter の型安全(スキーマ不一致はビルドエラー)
glob()loader で Markdown ファイルを自動収集getCollection()で全記事を取得して静的パスを生成
ビルドパイプライン
pnpm build
1. Astro: Markdown → HTML、getStaticPaths でページ列挙
2. Satori + resvg: 各記事の OGP PNG をビルド時に生成
3. @astrojs/sitemap: sitemap-index.xml を自動生成
4. Pagefind: dist/ を走査して検索インデックスを構築
→ dist/ に成果物が出揃う
関連
- 1. 🏗️このサイトのアーキテクチャ
- 2. 🏗️ブログサイト構築 — 全体アーキテクチャ
- 3. 🚀ブログサイト構築 — Astro SSG と MPA 設計
- 4. ☁️ブログサイト構築 — Cloudflare ホスティング
- 5. 🖼️ブログサイト構築 — OGP 画像の自動生成
- 6. 🎨ブログサイト構築 — Satori で絵文字を描画する
- 7. 🗄️ブログサイト構築 — Cloudflare D1 入門