Astro · LEARN AT YOUR OWN PACE今日も、一歩ずつ。

LESSON 09 · 約35分

Content Collectionsで記事を管理する

同じ形の記事を読み込み、一覧と本文ページを作ります。

コードを試す前に

02で作成する基本プロジェクトが前提です。各コード例は独立した学習用の例です。

各コードは独立した学習用の例で、すべてを順に一つのプロジェクトへ上書きする形式ではありません。同じレッスン内の対応ファイルは組み合わせます。依存関係・入力ファイル・実行条件は各noteに記載します。

開発環境の準備を確認する →

このレッスンでわかること

  • 現行のloaderを使ってコレクションを定義できる
  • getCollectionとrenderを使い分けられる
  • ビルド時とライブの取得を区別できる

記事をページのコードから分ける

記事が増えたら、タイトルなどの共通情報と本文をコンテンツとして管理します。現行方式はsrc/content.config.jsにdefineCollectionを置き、loaderで読み込む場所を指定します。globは複数のファイル、fileは一つのJSONなどを読み込む組み込みloaderです。昔のsrc/content/configやtype: "content"方式をこの例へ混ぜません。

schemaで欠落を早く見つける

schemaは入力データの形を検証するルールです。z.string()でタイトルの文字列を要求するなど、JavaScriptでも使えます。記事の書き忘れを公開前に発見するため、まず小さなルールから始めます。生成されたtsconfig.jsonは残します。base設定に変更した場合は公式案内のstrictNullChecksとallowJsが必要です。

取得・本文描画・URLは別の仕事

getCollection("notes")はentryの配列を返します。タイトルはentry.data.title、識別子はentry.idです。本文の描画にはastro:contentからimportしたrender(entry)を使い、返されたContentを表示します。コレクションを作るだけではURLは生成されず、getStaticPathsなどでページと結びつけます。旧entry.render()やentry.slugをそのまま使いません。

データの鮮度はloaderの種類で決まる

このレッスンのglob/fileはビルド時コレクションです。オンデマンドのページからgetCollectionを呼んでも、毎回元のCMSを取得し直すライブデータにはなりません。ライブコレクションは別のsrc/live.config.*、defineLiveCollection、getLiveCollection/getLiveEntryを使い、対応adapterとオンデマンド描画が必要です。

  • ライブloaderは組み込みのglob/fileとは別API。自作または対応する提供元のloaderを使う
  • ライブ取得は通信失敗・待ち時間・キャッシュを設計する。発展として必要なときに導入する

Markdownを読み込む設定

src/content.config.js
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';

const notes = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/data/notes' }),
  schema: z.object({
    title: z.string(),
    summary: z.string(),
  }),
});

export const collections = { notes };

現行公式ドキュメントのContent Layer方式です。.jsが利用できます。以下のMarkdownファイルと組み合わせます。

記事のメタデータと本文

src/data/notes/harbor.md
---
title: 港の散歩
summary: 朝の港を歩いた記録です。
---
## 朝の風景

小さな船が並び、静かな時間が流れていました。

このfrontmatterはJavaScriptではなくYAMLのメタデータです。schemaの必須項目をそろえます。

現行APIで本文ページを生成する

src/pages/notes/[...id].astro
---
import { getCollection, render } from 'astro:content';

export async function getStaticPaths() {
  const entries = await getCollection('notes');
  return entries.map((entry) => ({
    params: { id: entry.id },
    props: { entry },
  }));
}

const { entry } = Astro.props;
const { Content } = await render(entry);
---
<h1>{entry.data.title}</h1>
<p>{entry.data.summary}</p>
<Content />

このレッスンの設定と記事が必要です。06の[slug].astroとは独立した例であり、同じURLに競合させず置き換えて試します。[...id]はサブフォルダー由来のIDにも対応します。

TRY IT YOURSELF

記事を2件に増やす

  1. 異なるファイル名のMarkdownを追加する
  2. getCollectionの結果から一覧リンクを作る
  3. titleを一時的に欠落させ、ビルドで検証エラーが出ることを確かめる
  4. 修正して2件の詳細URLを直接開く

できたらOK:記事追加・検証・ページ生成の三つの役割を説明できる

QUICK CHECK

理解を確かめよう

現行のビルド時Content Collectionsで、ローカルの複数Markdownを読む組み込みloaderはどれ?

公式資料でもう少し詳しく

ここまで読めたら、ひとつ前進。