コンテンツコレクション
追加:
astro@2.0.0
コンテンツコレクションは、Astroプロジェクトでコンテンツを管理し、オーサリングするもっとも良い方法です。コレクションはドキュメントを整理し、フロントマターを検証、すべてのコンテンツに対して自動的にTypeScriptの型安全性を提供します。
コンテンツコレクションとは?
予約されているプロジェクトディレクトリsrc/content
の中にあるトップレベルのディレクトリは、1つのコンテンツコレクションを表わします。たとえばsrc/content/newsletter
やsrc/content/authors
などになります。src/content
ディレクトリの中に入れられるのは、コンテンツコレクションだけです。このディレクトリは他のものには使えません。
コレクションエントリーは、コンテンツコレクションディレクトリ内に保存されたコンテンツのことです。エントリーには、Markdown(.md
)やMDX (.mdx
MDXインテグレーション (EN)を使用)などのコンテンツオーサリングフォーマットや、YAML(.yaml
)やJSON(.json
)などのデータフォーマットを使用できます。コンテンツの検索と整理を容易にするため、ファイルには一貫性のある命名スキーム(小文字、スペースの代わりにダッシュ)の使用をおすすめしますが、これは必須ではありません。また、ファイル名の前にアンダースコア(_)を付けることで、ビルド対象からエントリーを除外できます。
ディレクトリsrc/content/
ディレクトリnewsletter/ “newsletter”コレクション
- week-1.md コレクションエントリー
- week-2.md コレクションエントリー
- week-3.md コレクションエントリー
コレクションができたら、Astroの組み込みコンテンツAPIを使ってコンテンツのクエリを始められます。
“.astro”ディレクトリ
Astroは、コンテンツコレクションの重要なメタデータを、プロジェクト内の.astro
ディレクトリに保存します。このディレクトリを維持または更新するために、何かする必要はありません。プロジェクトでの作業中は、このディレクトリを完全に無視してください。
.astro
ディレクトリは、astro dev
コマンドやastro build
コマンドを実行すると常に自動で更新されます。必要に応じてastro sync
を実行し、手動で.astro
ディレクトリを更新できます。
バージョン管理にGitを使っている場合は、.gitignore
に .astro
を追加して.astro
ディレクトリを無視することをおすすめします。以下は、このディレクトリとその中のファイルを無視するようにGitに伝えます。
echo "\n.astro" >> .gitignore
複数のコレクションによる整理
2つのファイルが異なる種類のコンテンツ(例えばブログの投稿と著者のプロフィール)を表す場合、それらは異なるコレクションに属する可能性が高いでしょう。多くの機能(フロントマターの検証、TypeScriptの自動型安全性)では、コレクション内のすべてのエントリーが同様の構造を共有する必要がありますので、これは重要です。
さまざまなタイプのコンテンツを扱うことになったら、それぞれのタイプを表す複数のコレクションを作成する必要があります。プロジェクトには、いくつでも異なるコレクションを作成できます。
ディレクトリsrc/content/
ディレクトリnewsletter/
- week-1.md
- week-2.md
ディレクトリblog/
- post-1.md
- post-2.md
ディレクトリauthors/
- grace-hopper.json
- alan-turing.json
サブディレクトリを使った整理
コンテンツコレクションは、常にsrc/content/
ディレクトリ内のトップレベルのフォルダです。コレクションを別のコレクションの中に入れ子にはできません。しかし、サブディレクトリを使ってコレクション内のコンテンツを整理できます。
たとえば、1つのdocs
コレクション内で多言語の翻訳を整理するために、次のディレクトリ構造を使えます。このコレクションをクエリするとき、ファイルパスを使用して言語によって結果をフィルタできます。
ディレクトリsrc/content/
ディレクトリdocs/ このコレクションは、言語別に整理したサブディレクトリを使用
ディレクトリen/
- …
ディレクトリes/
- …
ディレクトリde/
- …
コレクションの定義
src/content/config.ts
ファイルは省略できます。しかし、コレクションを定義しないと、フロントマターのスキーマ検証やTypeScriptの自動型付けなど、コレクションのいくつかの優れた機能が使えなくなります。
コンテンツコレクションを最大限に活用するには、プロジェクト内にsrc/content/config.ts
ファイルを作成してください(.js
と.mjs
の拡張子もサポートされています)。これは、Astroがコンテンツコレクションを設定するために自動的に読み込んで使用する特別なファイルです。
// 1. `astro:content`からユーティリティをインポートimport { defineCollection } from 'astro:content';// 2. コレクションを定義const blogCollection = defineCollection({ /* ... */ });// 3. コレクションを登録するために、単一の`collections`オブジェクトをエクスポート// このキーは、"src/content"のコレクションのディレクトリ名と一致する必要があります。export const collections = { 'blog': blogCollection,};
TypeScriptのセットアップ
tsconfig.json
ファイルでAstroのTypeScript設定をstrict
またはstrictest
に設定していない場合は、tsconfig.json
を更新してstrictNullChecks
を有効にする必要があるかもしれません。
{ // 注意:"astro/tsconfigs/strict"または"astro/tsconfigs/strictest"を使用する場合は変更する必要はありません。 "extends": "astro/tsconfigs/base", "compilerOptions": { "strictNullChecks": true }}
Astroプロジェクトで.js
または.mjs
ファイルを使用する場合、tsconfig.json
でallowJs
を有効にすることで、インテリセンスとエディタでの型チェックを有効にできます。
{ // 注意:"astro/tsconfigs/strict"または"astro/tsconfigs/strictest"を使用する場合は変更する必要はありません。 "extends": "astro/tsconfigs/base", "compilerOptions": { "strictNullChecks": true, "allowJs": true }}
コレクションスキーマの定義
スキーマは、コレクション内の一貫したフロントマターまたはエントリーデータを強制します。スキーマは、このデータへの参照やクエリが必要なときに、それが予測可能な形で存在することを保証します。ファイルがコレクションスキーマに違反している場合、Astroは役にたつエラーを表示してお知らせします。
スキーマはAstroのコンテンツに対する自動的なTypeScriptの型付けにも力を発揮します。コレクションにスキーマを定義すると、Astroは自動的にTypeScriptインターフェイスを生成して適用します。その結果、コレクションをクエリする際には、プロパティの自動補完や型チェックを含むTypeScriptが完全にサポートされます。
最初のコレクションを定義するには、src/content/config.ts
ファイルがまだ存在しなければ、このファイルを作成します(.js
と .mjs
の拡張子もサポートされています)。
astro:content
から適切なユーティリティをインポートします。- 検証したい各コレクションを定義します。これには、コレクションがMarkdownのようなコンテンツオーサリングフォーマット(
type: 'content'
)であるか、JSONやYAMLのようなデータフォーマット(type: 'data'
)であるかを指定するtype
(Astro v2.5.0で導入)が含まれます。また、フロントマターやエントリーデータの形を定義するschema
も含まれます。 - コレクションを登録するために、単一の
collections
オブジェクトをエクスポートします。
// 1. ユーティリティを`astro:content`からインポートimport { z, defineCollection } from 'astro:content';
// 2. 各コレクションに`type`と`schema`を定義const blogCollection = defineCollection({ type: 'content', // v2.5.0以降 schema: z.object({ title: z.string(), tags: z.array(z.string()), image: z.string().optional(), }),});
// 3. コレクションを登録するために、単一の`collections`オブジェクトをエクスポートexport const collections = { 'blog': blogCollection,};
複数のコレクションの定義
defineCollection()
は、複数のスキーマを作成するために何度でも使えます。 すべてのコレクションは、単一のcollections
オブジェクトの内部からエクスポートする必要があります。
const blogCollection = defineCollection({ type: 'content', schema: z.object({ /* ... */ })});const newsletter = defineCollection({ type: 'content', schema: z.object({ /* ... */ })});const authors = defineCollection({ type: 'data', schema: z.object({ /* ... */ })});
export const collections = { 'blog': blogCollection, 'newsletter': newsletter, 'authors': authors,};
プロジェクトが成長するにつれて、コードベースを再編成し、src/content/config.ts
ファイルからロジックを移動することも自由にできます。スキーマを別々に定義することは、複数のコレクションでスキーマを再利用したり、プロジェクトの他の部分とスキーマを共有したりするのに便利です。
// 1. ユーティリティとスキーマのインポートimport { defineCollection } from 'astro:content';import { blogSchema, authorSchema } from '../schemas';
// 2. コレクションを定義const blogCollection = defineCollection({ type: 'content', schema: blogSchema,});const authorCollection = defineCollection({ type: 'data', schema: authorSchema,});
// 3. 複数のコレクションをエクスポートして登録export const collections = { 'blog': blogCollection, 'authors': authorCollection,};
サードパーティのコレクションスキーマの使用
外部のnpmパッケージなど、どこからでもコレクションスキーマをインポートできます。これは、独自のコレクションスキーマを提供するテーマやライブラリを使用するときに便利です。
import { blogSchema } from 'my-blog-theme';const blogCollection = defineCollection({ type: 'content', schema: blogSchema });
// 'my-blog-theme'の外部スキーマを使用して、ブログコレクションをエクスポートexport const collections = { 'blog': blogCollection,};
Zodによるデータ型の定義
AstroはZodを使ってコンテンツスキーマを動かしています。Zodを利用すると、Astroはコレクション内のすべてのファイルのフロントマターを検証し、プロジェクト内からコンテンツをクエリする際に自動的にTypeScriptの型を提供できます。
AstroでZodを使うには、"astro:content"
からz
ユーティリティをインポートします。これはZodライブラリの再エクスポートで、Zodのすべての機能をサポートしています。Zodがどのように動作し、どのような機能が利用可能かについての完全なドキュメントは、ZodのREADMEを参照してください。
// 例:一般的なZodのデータ型のチートシートimport { z, defineCollection } from 'astro:content';
defineCollection({ schema: z.object({ isDraft: z.boolean(), title: z.string(), sortOrder: z.number(), image: z.object({ src: z.string(), alt: z.string(), }), author: z.string().default('Anonymous'), language: z.enum(['en', 'es']), tags: z.array(z.string()), // オプションのフロントマター・プロパティ。非常に一般的です! footnote: z.string().optional(), // フロントマターでは、引用符で囲まずに書かれた日付はDateオブジェクトとして解釈されます。 publishDate: z.date(), // 日付文字列(例えば "2022-07-08")をDateオブジェクトに変換できます。 // publishDate: z.string().transform((str) => new Date(str)), // アドバンスド:文字列が電子メールであることを検証する。 authorContact: z.string().email(), // アドバンスド:文字列がURLであることを検証する。 canonicalURL: z.string().url(), })})
コレクション参照の定義
コレクションエントリーは、他の関連するエントリーを「参照」することもできます。
Collections APIのreference()
関数を使うと、コレクションスキーマのプロパティを別のコレクションのエントリーとして定義できます。たとえば、すべてのspace-shuttle
エントリーに、型チェック、オートコンプリート、バリデーションにpilot
コレクションのスキーマを使用するpilot
プロパティを含めるように要求できます。
よくある例は、JSONとして保存された再利用可能な著者プロフィールや、同じコレクションに保存された関連投稿URLを参照するブログ投稿です。
import { defineCollection, reference, z } from 'astro:content';
const blog = defineCollection({ type: 'content', schema: z.object({ title: z.string(), // `authors` コレクションから `id` で1人の著者を参照 author: reference('authors'), // `blog`コレクションから`slug`による関連記事の配列を参照 relatedPosts: z.array(reference('blog')), })});
const authors = defineCollection({ type: 'data', schema: z.object({ name: z.string(), portfolio: z.string().url(), })});
export const collections = { blog, authors };
このブログ記事の例では、関連記事のslug
と投稿者のid
を指定しています。
---title: "私のブログへようこそ"author: ben-holmes # `src/content/authors/ben-holmes.json`を参照relatedPosts:- about-me # `src/content/blog/about-me.md`を参照- my-year-in-review # `src/content/blog/my-year-in-review.md`を参照---
カスタムスラグの定義
type: 'content'
を使用している場合、すべてのコンテンツエントリーはファイルid
(EN)からURLフレンドリーなslug
プロパティを生成します。このスラグは、コレクションからエントリーを直接クエリするために使用されます。また、コンテンツから新しいページやURLを作成するときにも便利です。
ファイルのフロントマターに独自のslug
プロパティを追加すると、エントリーの生成されたスラグをオーバーライドできます。これは他のWebフレームワークの”permalink”機能に似ています。"slug"
は特別な予約されたプロパティ名で、カスタムコレクションschema
では許可されず、エントリーのdata
プロパティには表示されません。
---title: 私のブログ記事slug: my-custom-slug/supports/slashes---あなたのブログ記事の内容はこちら。
コレクションのクエリ
Astroには、コレクションにクエリを発行して1つ(または複数)のコンテンツエントリーを返す関数が2つあります。getCollection()
(EN)とgetEntry()
(EN)です。
import { getCollection, getEntry } from 'astro:content';
// コレクションからすべてのエントリーを取得します。// 引数としてコレクション名が必要です。// 例: `src/content/blog/**`を取得する。const allBlogPosts = await getCollection('blog');
// コレクションから単一のエントリーを取得します。// コレクションの名前と、以下のいずれかが必要です。// エントリーの`slug`(コンテンツコレクション)または`id`(データコレクション)を指定する。// 例: `src/content/authors/grace-hopper.json`を取得する。const graceHopperProfile = await getEntry('authors', 'grace-hopper');
どちらの関数も、CollectionEntry
(EN)型で定義されたコンテンツエントリーを返します。
参照データへのアクセス
スキーマで定義されている参照は、最初にコレクションエントリーをクエリした後で、個別にクエリする必要があります。getEntry()
関数を再度使用するか、またはgetEntries()
を使用して、返されたdata
オブジェクトから参照されるエントリーを取得できます。
---import { getEntry, getEntries } from 'astro:content';
const blogPost = await getEntry('blog', 'welcome');
// 単一参照の解決const author = await getEntry(blogPost.data.author);// 参照の配列を解決const relatedPosts = await getEntries(blogPost.data.relatedPosts);---
<h1>{blogPost.data.title}</h1><p>著者: {author.data.name}</p>
<!-- ... -->
<h2>次もおすすめ</h2>{relatedPosts.map(p => ( <a href={p.slug}>{p.data.title}</a>))}
コレクションクエリのフィルタリング
getCollection()
はオプションの”filter”コールバックを受け取り、エントリーのid
やdata
(フロントマター)プロパティに基づいてクエリをフィルタリングします。type: 'content'
のコレクションについては、slug
に基づいてフィルタリングもできます。
slug
プロパティはコンテンツコレクションに固有のもので、JSONやYAMLのコレクションをフィルタリングするときには利用できません。
これを使用して、コンテンツを好きな基準でフィルタリングできます。たとえば、draft
のようなプロパティでフィルタリングして、下書きのブログ記事がブログに公開されないようにできます。
// 例: `draft: true`を含むエントリーを除外import { getCollection } from 'astro:content';const publishedBlogEntries = await getCollection('blog', ({ data }) => { return data.draft !== true;});
開発サーバーの実行時にのみ閲覧可能で、本番用にはビルドされない下書き(draft)ページも作成できます。
// 例: 本番用にビルドするときにのみ、`draft: true`を含むエントリーを除外import { getCollection } from 'astro:content';const blogEntries = await getCollection('blog', ({ data }) => { return import.meta.env.PROD ? data.draft !== true : true;});
filterの引数は、コレクション内の入れ子ディレクトリによるフィルタリングもサポートします。id
にはネストされた完全なパスが含まれるので、各id
の先頭でフィルタリングして、特定のネストされたディレクトリからのアイテムだけを返せます。
// 例: コレクション内のサブディレクトリによるエントリーのフィルタリングimport { getCollection } from 'astro:content';const englishDocsEntries = await getCollection('docs', ({ id }) => { return id.startsWith('en/');});
Astroテンプレートでのコンテンツの使用
コレクションエントリーをクエリすると、Astroコンポーネントテンプレートの内部で各エントリーに直接アクセスできます。これにより、コンテンツへのリンク(コンテンツslug
を使用)やコンテンツに関する情報(data
プロパティを使用)などのHTMLをレンダリングできます。
コンテンツをHTMLにレンダリングする方法については、下記のコンテンツをHTMLにレンダリングするを参照してください。
---import { getCollection } from 'astro:content';const blogEntries = await getCollection('blog');---<ul> {blogEntries.map(blogPostEntry => ( <li> <a href={`/my-blog-url/${blogPostEntry.slug}`}>{blogPostEntry.data.title}</a> <time datetime={blogPostEntry.data.publishedDate.toISOString()}> {blogPostEntry.data.publishedDate.toDateString()} </time> </li> ))}</ul>
コンテンツをプロパティとして渡す
コンポーネントは、コンテンツエントリー全体をプロパティとして渡すこともできます。
この場合、CollectionEntry
(EN)ユーティリティを使用して、TypeScriptでコンポーネントのプロパティを適切に型付けできます。 このユーティリティは、コレクションスキーマの名前と一致する文字列引数を取り、そのコレクションのスキーマのすべてのプロパティを継承します。
---import type { CollectionEntry } from 'astro:content';interface Props { post: CollectionEntry<'blog'>;}
// `post`は'blog'コレクションのスキーマタイプにマッチする。const { post } = Astro.props;---
コンテンツをHTMLにレンダリングする
クエリされたエントリーのrender()
関数プロパティを使用して、MarkdownおよびMDXエントリーをHTMLにレンダリングできます。この関数を呼び出すと、<Content />
コンポーネントとレンダリングされたすべての見出しのリストを含む、レンダリングされたコンテンツとメタデータにアクセスできます。
---import { getEntry } from 'astro:content';const entry = await getEntry('blog', 'post-1');const { Content, headings } = await entry.render();---<p>公開日: {entry.data.published.toDateString()}</p><Content />
コンテンツからルーティングを生成
コンテンツコレクションはsrc/pages/
ディレクトリの外に保存されます。つまり、デフォルトではコレクション項目に対してルーティングは生成されません。コレクション項目からHTMLページを生成するには、手動で新しい動的ルーティングを作成する必要があります。動的ルーティングは、リクエストのパラメーター (例:src/pages/blog/[...slug].astro
のAstro.params.slug
) をマッピングして、コレクション内の正しいエントリーを取得します。
ルーティングを生成する正確な方法は、ビルドの出力モードによって異なります。モードはstatic(デフォルト)またはserver(SSRの場合)です。
静的出力のビルド(デフォルト)
静的なウェブサイトを構築する場合(Astroのデフォルトの動作)、ビルド中に1つのsrc/pages/
コンポーネントから複数のページを作成するには、getStaticPaths()
(EN)関数を使用します。
getStaticPaths()
内でgetCollection()
(EN) を呼び出し、コンテンツをクエリします。それから、各コンテンツエントリーのslug
プロパティを使用して、新しいURLパスを作成します。
---import { getCollection } from 'astro:content';// 1. コレクションエントリーごとに新しいパスを生成export async function getStaticPaths() { const blogEntries = await getCollection('blog'); return blogEntries.map(entry => ({ params: { slug: entry.slug }, props: { entry }, }));}// 2. テンプレートでは、プロパティからエントリーを直接取得できるconst { entry } = Astro.props;const { Content } = await entry.render();---<h1>{entry.data.title}</h1><Content />
これにより、blog
コレクションの各エントリーに新しいページが生成されます。たとえば、src/content/blog/hello-world.md
のエントリーはhello-world
というスラグを持つので、最終的なURLは/posts/hello-world/
となります。
カスタムスラグに/
文字が含まれ、複数のパスセグメントを持つURLを生成する場合は、この動的ルーティングページの.astro
ファイル名にレストパラメータ([...slug]
)を使用する必要があります。
サーバー出力のビルド(SSR)
動的なウェブサイトを構築する場合(AstroのSSRサポートを使用する場合)、ビルド時にパスを生成する必要はありません。そのかわり、ページでは(Astro.request
あるいはAstro.params
を使って)リクエストを調べてslug
を見つけ、getEntry()
(EN)を使って取得しなければなりません。
---import { getEntry } from "astro:content";// 1. 受信サーバーのリクエストからスラグを取得const { slug } = Astro.params;if (slug === undefined) { throw new Error("Slug is required");}// 2. リクエストスラグを使ってエントリーを直接検索const entry = await getEntry("blog", slug);// 3. エントリーが存在しない場合はリダイレクトif (entry === undefined) { return Astro.redirect("/404");}// 4. (オプション) テンプレート内でエントリーをHTMLにレンダリングconst { Content } = await entry.render();---
ファイルベースのルーティングからの移行
このガイドでは、src/pages/
フォルダにMarkdownファイルがある既存のAstroプロジェクトを、コンテンツコレクションに変換する方法を紹介します。ブログを作るチュートリアルの完成したプロジェクトを例として使用しています。
-
Astro v2.0以降にアップグレードし、すべてのインテグレーションを最新バージョンにアップグレードしてください。
-
コンテンツコレクションにTypeScriptを設定します。
-
少なくとも1つのコレクション(
src/content/
内のフォルダ)を作成し、MarkdownページとMDXページをsrc/pages/
からsrc/content/
のこれらのサブディレクトリに移動します。コレクションは、同じコレクション内のすべてのファイルのフロントマタープロパティが似ている場合に、もっともうまく機能します。ですから、同じような種類のページを反映するように、新しいフォルダ構造を選択してください。たとえば、チュートリアルのブログ記事を移行するには、
src/pages/posts/
の内容をsrc/content/posts/
に移動します。 -
src/content/config.ts
ファイルを作成し、各コンテンツタイプのスキーマを定義します。ブログの場合、コンテンツタイプはposts
だけです。src/content/config.ts // ユーティリティを`astro:content`からインポートimport { z, defineCollection } from "astro:content";// 各コレクションに`type`と`schema`を定義const postsCollection = defineCollection({type: 'content',schema: z.object({title: z.string(),pubDate: z.date(),description: z.string(),author: z.string(),image: z.object({url: z.string(),alt: z.string()}),tags: z.array(z.string())})});// コレクションを登録するために、単一の`collections`オブジェクトをエクスポートexport const collections = {posts: postsCollection,};エディターが
astro:content
を認識しない場合は、Astroが最新バージョンであることを確認し、開発サーバーを再起動してみてください。 -
コレクションからルートを生成します。コレクション内では、MarkdownファイルやMDXファイルは、Astroのファイルベースルーティングを使用しても自動的にページにならないため、自分でページを生成する必要があります。
このチュートリアルの場合は、
src/pages/posts/[...slug].astro
を作成します。このページは動的ルーティングを使用して、各コレクションのエントリーのページを生成します。このページはまた、ページスラグを取得し、各ルーティングでページコンテンツを利用できるようにするために、コレクションにクエリする必要があります。
投稿の
<Content />
を、MarkdownまたはMDXページのレイアウト内にレンダリングします。これにより、すべての投稿に共通のレイアウトを指定できます。src/pages/posts/[...slug].astro ---import { getCollection } from 'astro:content';import MarkdownPostLayout from '../../layouts/MarkdownPostLayout.astro';export async function getStaticPaths() {const blogEntries = await getCollection('posts');return blogEntries.map(entry => ({params: { slug: entry.slug }, props: { entry },}));}const { entry } = Astro.props;const { Content } = await entry.render();---<MarkdownPostLayout frontmatter={entry.data}><Content /></MarkdownPostLayout> -
個々の投稿のフロントマター内の
layout
定義を削除します。レンダリング時にコンテンツがレイアウトに包まれるようになり、このプロパティは不要になりました。src/content/post-1.md ---layout: ../../layouts/MarkdownPostLayout.astrotitle: 'はじめてのブログ記事'pubDate: 2022-07-01...--- -
Astro.glob()
をgetCollection()
(EN)に置き換えて、Markdownファイルからコンテンツとメタデータを取得します。また、返された post オブジェクトへの参照も更新する必要があります。なぜなら、data
プロパティにフロントマターの値が格納されるからです。チュートリアルのブログのインデックスページには、各記事のカードがリストアップされています。これは次のようになります。
src/pages/blog.astro ---import { getCollection } from "astro:content";import BaseLayout from "../layouts/BaseLayout.astro";import BlogPost from "../components/BlogPost.astro";const pageTitle = "私のAstro学習ブログ";const allPosts = await getCollection("posts");---<BaseLayout pageTitle={pageTitle}><p>ここには、私がAstroを学んでいく旅の様子を投稿します。</p><ul>{allPosts.map((post) => (<BlogPost url={'/posts/' + post.slug} title={post.data.title} />))}</ul></BaseLayout>チュートリアルのブログのプロジェクトは、各タグのページも動的に生成します。このページは次のようになります。
src/pages/tags/[tag].astro ---import { getCollection } from "astro:content";import BaseLayout from "../../layouts/BaseLayout.astro";import BlogPost from "../../components/BlogPost.astro";export async function getStaticPaths() {const allPosts = await getCollection("posts");const uniqueTags = [...new Set(allPosts.map((post) => post.data.tags).flat())];return uniqueTags.map((tag) => {const filteredPosts = allPosts.filter((post) =>post.data.tags.includes(tag));return {params: { tag },props: { posts: filteredPosts },};});}const { tag } = Astro.params;const { posts } = Astro.props;---<BaseLayout pageTitle={tag}><p>{tag}のタグが付いた記事</p><ul>{ posts.map((post) => <BlogPost url={'/posts/' + post.slug} title={post.data.title} />) }</ul></BaseLayout>同じロジックがタグインデックスページにも現れています。次のようになります。
src/pages/tags/index.astro ---import { getCollection } from "astro:content";import BaseLayout from "../../layouts/BaseLayout.astro";const allPosts = await getCollection("posts");const tags = [...new Set(allPosts.map((post) => post.data.tags).flat())];const pageTitle = "タグインデックス";---...個々のMarkdownやMDXファイルのインポートは、
getEntry()
(EN)で置き換える必要があります。 -
layouts/MarkdownPostLayout.astro
ファイルの公開日を使用するコードを更新しました。以前は、
pubDate
は文字列でした。投稿のフロントマターに型を導入した結果、pubDate
はDate
になりました。 日付をレンダリングするには、文字列に変換します。src/layouts/MarkdownPostLayout.astro ...<BaseLayout pageTitle={frontmatter.title}><p>{frontmatter.pubDate.toDateString()}</p><p><em>{frontmatter.description}</em></p><p>著者: {frontmatter.author}</p><img src={frontmatter.image.url} width="300" alt={frontmatter.image.alt} />...最後に、チュートリアルのブログプロジェクトにはRSSフィードが含まれています。この関数も
getCollection
とdata
オブジェクトを使用し、非同期関数に変換する必要があります。src/pages/rss.xml.js import rss from "@astrojs/rss";import { getCollection } from "astro:content";export async function GET() {const posts = await getCollection('posts');return rss({title: 'Astro学習者 | ブログ',description: 'Astroを学ぶ旅',site: 'https://my-blog-site.netlify.app',items: posts.map((post) => ({title: post.data.title,pubDate: post.data.pubDate,description: post.data.description,link: `/posts/${post.slug}/`,})),customData: `<language>ja-jp</language>`,});}
コンテンツコレクションを使用したブログチュートリアルの完全なサンプルについては、チュートリアルリポジトリのコンテンツコレクションのブランチを参照してください。
Remarkでフロントマターを修正
非推奨。Remarkとrehypeプラグインは生のMarkdownまたはMDXドキュメントのフロントマターにアクセスします。これはremarkPluginFrontmatter
のフロントマターがあなたの型セーフschema
とは別に扱われ、Astroを通して適用された変更やデフォルトは反映されません。使用は自己責任です!
Astroは、フロントマターを直接変更するremarkまたはrehypeプラグインをサポートしています。render()
から返されるremarkPluginFrontmatter
プロパティを使うと、コンテンツエントリー内でこの変更されたフロントマターにアクセスできます。
---import { getEntry } from 'astro:content';const blogPost = await getEntry('blog', 'post-1');const { remarkPluginFrontmatter } = await blogPost.render();---<p>{blogPost.data.title} — {remarkPluginFrontmatter.readingTime}</p>
![](/houston_chef.webp)
remarkやrehypeのパイプラインは、コンテンツがレンダリングされたときにのみ実行されます。そのため、render()
をコールした後にremarkPluginFrontmatter
を使用できるようになります。対照的に、getCollection()
やgetEntry()
はコンテンツをレンダリングしないので、これらの値を直接返すことはできません。