🚀

Astro でネストされた動的ルーティングを実現する

2022/10/31に公開

先日、 静的サイトジェネレータである Astro と microCMS を利用して、個人ブログを制作しました(Gatsby からの作り直し)。

今回のブログでは記事ごとにカテゴリを設定し、カテゴリ別の記事一覧ページを設けました。また、それらのページは10記事ずつページ分割しました。

たとえば、URL の例は以下のようになります。

今回は、このルーティングの実装方法について解説します。

Astro のルーティングについて

前提として、 Astro はファイルベースのルーティングを採用しており、src/pages/ 配下のディレクトリを元にページを生成します。 Next.js と同様なので、使ったことがある人には馴染みがあるかもしれません。

https://docs.astro.build/ja/core-concepts/routing/

動的ルーティングも Next.js とほぼ同様で、以下のルールに則れば実現できます。

  1. [ブラケット]記法を用いて、動的なパラメーターを識別する。
  2. getStaticPaths()関数をエクスポートして、Astroでプリレンダリングされるパスを正確に指定します。

https://docs.astro.build/ja/core-concepts/routing/#動的ルーティング

実装する

今回は、冒頭で記載した 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サイトにおいて、実際に実装してみると結構めんどくさい動的ルーティングの実装をご紹介しました。
あくまで実装例のひとつですが、なにかしら参考になれば幸いです。

参考

https://astro.build/
https://blog.microcms.io/astro-microcms-introduction/

Discussion