Astro でネストされた動的ルーティングを実現する
先日、 静的サイトジェネレータである Astro と microCMS を利用して、個人ブログを制作しました(Gatsby からの作り直し)。
今回のブログでは記事ごとにカテゴリを設定し、カテゴリ別の記事一覧ページを設けました。また、それらのページは10記事ずつページ分割しました。
たとえば、URL の例は以下のようになります。
-
https://shimotsu.netlify.app/category/work/page/1/ (カテゴリ:
work
の1ページ目) -
https://shimotsu.netlify.app/category/career/page/1/ (カテゴリ:
career
の1ページ目)
今回は、このルーティングの実装方法について解説します。
Astro のルーティングについて
前提として、 Astro はファイルベースのルーティングを採用しており、src/pages/
配下のディレクトリを元にページを生成します。 Next.js と同様なので、使ったことがある人には馴染みがあるかもしれません。
動的ルーティングも Next.js とほぼ同様で、以下のルールに則れば実現できます。
- [ブラケット]記法を用いて、動的なパラメーターを識別する。
- getStaticPaths()関数をエクスポートして、Astroでプリレンダリングされるパスを正確に指定します。
実装する
今回は、冒頭で記載した URL に対応するルーティングのために、 src/pages/category/[categorySlug]/page/[pageNum].astro
というディレクトリおよびファイルを用意しました。
categorySlug
には、 work
や career
といったカテゴリを識別するためのスラッグを、 pageNum
には現在のページ数を示すための数値を入力するようにします。
まず、ルーティングに必要な部分となる、 [pageNum].astro
内の getStaticPaths()
の全体像をコピペします。以降で、各所について解説します。
export async function getStaticPaths() {
// ①...API から全カテゴリを取得する
const { contents: categoryList } = await getCategories({
limit: MAX_CONTENTS_SIZE,
fields: ['slug', 'id'],
})
// ②...前段階となる配列を定義しておく
const temporaryArray: { category: string; page: number }[] = []
// ③...1で得たカテゴリ一覧を元に、2で用意した配列に値を追加していく
for (const category of categoryList) {
const { contents } = await getSameCategoryBlogs(category)
temporaryArray.push({
category: category.slug,
page: Math.ceil(contents.length / SHOW_PER_PAGE),
})
}
// ④...最終的に return する配列を定義しておく
const resultArray: { params: { categorySlug: string; pageNum: string } }[] =
[]
// ⑤...3で生成した temporaryArray を元に、最終的なデータを生成する
temporaryArray.forEach(item => {
for (let j = 0; j < item.page; j++) {
resultArray.push({
params: {
categorySlug: item.category,
pageNum: String(j + 1),
},
})
}
})
return resultArray
}
1について
ルーティングのために全カテゴリのデータが必要なので、最初に取得しておきます。
2について
最終的な戻り値となる配列を一発で生成するとコードが複雑になるので、簡単のためにひとつの段階として、{ category: string; page: number }[]
という型を持つ配列を定義しておきます。
これは、どのカテゴリが現在何ページ分の記事を持っているかを分かりやすく一覧化するためのデータです。
3について
1で得たカテゴリ一覧を元に、2で用意した配列 temporaryArray
に値を追加していきます。
この段階で、 temporaryArray
をログに出力すると、以下のようになっています。
[
{ category: 'gadget', page: 1 },
{ category: 'blog', page: 1 },
{ category: 'career', page: 1 },
{ category: 'childcare', page: 1 },
{ category: 'life', page: 2 },
{ category: 'work', page: 4 }
]
4について
ここで、最終的に戻り値としたい配列を定義しておきます。
ここで定義される categorySlug
および pageNum
は、ディレクトリで [ブラケット]
になっている箇所と合わせる必要があります。
5について
3で生成した temporaryArray
を元に、最終的なデータを生成します。この段階で、全てのカテゴリのページ数分の要素を持つ配列が完成します。
[
{ params: { categorySlug: 'gadget', pageNum: '1' } },
{ params: { categorySlug: 'blog', pageNum: '1' } },
{ params: { categorySlug: 'career', pageNum: '1' } },
{ params: { categorySlug: 'childcare', pageNum: '1' } },
{ params: { categorySlug: 'life', pageNum: '1' } },
{ params: { categorySlug: 'life', pageNum: '2' } },
{ params: { categorySlug: 'work', pageNum: '1' } },
{ params: { categorySlug: 'work', pageNum: '2' } },
{ params: { categorySlug: 'work', pageNum: '3' } },
{ params: { categorySlug: 'work', pageNum: '4' } }
]
デプロイ
これでデプロイすると、無事以下のように静的なページが生成されました🎉
(...略)
9:46:28 PM: generating static routes
9:46:28 PM: ▶ src/pages/index.astro
9:46:32 PM: └─ /index.html (+3.77s)
9:46:32 PM: ▶ src/pages/category/[categorySlug]/page/[pageNum].astro
9:46:51 PM: ├─ /category/gadget/page/1/index.html (+18.85s)
9:46:57 PM: ├─ /category/blog/page/1/index.html (+25.17s)
9:47:04 PM: ├─ /category/career/page/1/index.html (+31.83s)
9:47:11 PM: ├─ /category/childcare/page/1/index.html (+38.85s)
9:47:18 PM: ├─ /category/life/page/1/index.html (+46.16s)
9:47:25 PM: └─ /category/work/page/1/index.html (+53.52s)
(...略)
さいごに
というわけで、Jamstack 構成のWebサイトにおいて、実際に実装してみると結構めんどくさい動的ルーティングの実装をご紹介しました。
あくまで実装例のひとつですが、なにかしら参考になれば幸いです。
参考
Discussion