Astroチュートリアルメモ その5-2,3【動的ルーティングとタグ一覧ページ】
タグのページを作る際に、動的ルーティングを使う。
src/pages/tags/[tag].astro
のようにファイルを作り、取りうるURLをgetStaticPaths
で指定する。
Next.jsのgetStaticPaths
とURLの作り方は同じ。
大きな違いはAstroのgetStaticPaths
はNext.jsのgetStaticPaths
とgetStaticProps
を合わせた機能を持っているところ。
動的ページの作成
---
import BaseLayout from "../../layouts/BaseLayout.astro";
import BlogPost from "../../components/BlogPost.astro";
export async function getStaticPaths({}) {
const allPosts = await Astro.glob("../posts/*.md");
return [
{ params: { tag: "astro" }, props: { posts: allPosts } },
{ params: { tag: "successes" }, props: { posts: allPosts } },
{ params: { tag: "community" }, props: { posts: allPosts } },
{ params: { tag: "blogging" }, props: { posts: allPosts } },
{ params: { tag: "setbacks" }, props: { posts: allPosts } },
{ params: { tag: "learning in public" }, props: { posts: allPosts } },
];
}
const { tag } = Astro.params;
const { posts } = Astro.props;
const filteredPosts = posts.filter((post) =>
post.frontmatter.tags.includes(tag)
);
---
AstroのgetStaticPaths
の作り方は基本はNext.jsのものと同じ。async awaitを使う。
params: { tag: "astro" }
のオブジェクトとして、動的ルーティング用のIDとURLになりうる文字列の一覧を渡す。
さらに、getStaticPaths
のreturnの中で、{ params: { tag: "astro" }, props: { posts: allPosts } },
のように、記事データをpropsとして渡すことができる。
ページ内でpropsを使う
ページ内でparamsとpropsを使うためにオブジェクトを定義する。
params
をtag
として、props
をposts
として読み込む。
さらに、filter
で動的ルーティングで指定されたタグを含むマークダウンファイルの記事データだけをfilteredPosts
に読み込むことで、該当タグの一覧ページを作る。
---
//抜粋
const { tag } = Astro.params;
const { posts } = Astro.props;
const filteredPosts = posts.filter((post) =>
post.frontmatter.tags.includes(tag)
);
---
読み込んだtag
とfilteredPosts
を表示部分の記述に書くことで、タグの一覧が完成する。
<BaseLayout pageTitle={tag}>
<p>Posts tagged with {tag}</p>
<ul>
{
filteredPosts.map((post) => (
<BlogPost url={post.url} title={post.frontmatter.title} />
))
}
</ul>
</BaseLayout>
Next.jsでタグ一覧を作ろうとすると、getStaticProps
で該当のデータを持ってきてそれをフィルターして、getStaticProps
のreturnで一覧をだして…となり、その一連の流れは全て自分で書くことになるが、Astroの場合は一部Astroの機能をそのまま使うことができる。
Astro.glob
でマークダウンファイル一覧を作ることができ、getStaticPaths
で取りうるURLの一覧だけでなく、記事データも渡すことができる。上のチュートリアルの例では、その記事データをgetStaticPaths
の外側で整形している。
Astroはマークダウンファイルを使ってブログを作る場合、カテゴリやタグを簡単に実装できるように最初から機能が作られているのが、Next.jsとのおおきな違い。ただ、別にNext.jsでも自分で書けば全部できるし、自分の好きなようにカスタマイズできる。
静的なページをさっと作るのにAstroは向いているっぽいのはこういう部分が作りこまれているからかなと思う。
タグを自動で取得してparamsを作る
上ではgetStaticPaths
にタグと読み込む記事データを全てべた書きしているが、タグのデータはマークダウンファイル内に自由に書き込めるし、記事データも存在している。
すでに必要な全部のデータが、マークダウンの中に存在しているので、データから自動でparams
を作るほうが直感的というか、わかりやすいというか。自分がNext.jsでタグ一覧を作ったときもそんな風に作った記憶がある。
というわけで、チュートリアル通りにgetStaticPaths
を書き換える。
上ではgetStaticPaths
の外でアクセスのあったタグ名に対応するデータを整形していたが、その処理をgetStaticPaths
内で先に自動で行う。
export async function getStaticPaths({}) {
const allPosts = await Astro.glob("../posts/*.md");
const uniqueTags = [
...new Set(allPosts.map((post) => post.frontmatter.tags).flat()),
];
return uniqueTags.map((tag) => {
const filteredPosts = allPosts.filter((post) =>
post.frontmatter.tags.includes(tag)
);
return {
params: { tag },
props: { posts: filteredPosts },
};
});
}
uniqueTags
では、Astro.glob
で読み込んだallPosts
のタグの配列を作り、それをflat
で1次元にして、Set
オブジェクトで重複を無くしている。最終的にSet
オブジェクトをスプレッド構文で配列にしている。
[
'astro',
'blogging',
'learning in public',
'successes',
'setbacks',
'community'
]
return
も書き換える。
先ほどは全てのタグに全ての記事データを送っていたが、各タグに対応する記事データだけをprops
に設定する。
タグに該当する記事データだけをfilteredPosts
に入れる形。
以前Next.jsでブログを作ったときに、自身でタグ機能を実装したが、基本はこれと同じ作り方をしたので、非常になじみ深いというか、理解しやすかった。
ブログの動的ルーティングの場合、基本的にurlに紐づいて必要な記事データも決まるわけだから、getStaticPaths
内で完結させればいいよね的な思想は、なんとなく理解できる。
ブログ以外のサイトを作る場合はどういう形になるのか、どんな使い方を試したくなるのかは、そのケースにならないとわからなそう。Astroの機能だけで完結できるのか、別のことをする必要があるかは今後使ってみての課題。
タグ一覧ページを作る
動的ルーティングで作った各タグページへの導線(タグ一覧)を作る。
Next.jsと同じで、動的ルーティングのフォルダの中にindex.astro
を作ることでタグのトップを作ることができる。
├─tags
│ ├─[tag].astro
│ └─index.astro
上の場合、[tag].astro
でドメイン/tags/タグ名
ページに、index.astro
でドメイン/tags/
になる。
スタイルシートは割愛。クラスをつけているのはチュートリアルの例ではcssを当てているため。
---
import BaseLayout from '../../layouts/BaseLayout.astro';
const allPosts = await Astro.glob('../posts/*.md');
const tags = [...new Set(allPosts.map((post) => post.frontmatter.tags).flat())];
const pageTitle = "Tag Index";
---
<BaseLayout pageTitle={pageTitle}>
<div class="tags">
{tags.map((tag) => (
<p class="tag"><a href={`/tags/${tag}`}>{tag}</a></p>
))}
</div>
</BaseLayout>
何度も出てきているAstro.glob
で全てのマークダウンファイルのデータを取得し、記事データから重複無しのタグ一覧を作り、mapでタグ名の一覧を表示する。
この辺りもNext.jsで自分で作ったタグ一覧やカテゴリ一覧と同じ作り方なのでわかりやすかった。
次の記事
Discussion