Hiratake Web ロゴ

Nuxtの動的ルーティングで一覧画面のページングに共通のVueファイルを使う

投稿した日
更新した日
書いたひと
icon
ひらたけ

ブログなどを構築する際、投稿の一覧画面にページングの機能を実装することがよくあると思います。Nuxt で実装しているウェブサイトで一覧画面の 1 ページ目を「/blog」、2 ページ目以降を「/blog/page/2」としたい場合に、/pages ディレクトリに配置した共通の Vue ファイルを使用しようとして詰まったので、解決方法を備忘録的に残しておこうと思います。

環境

実装時に使用した Nuxt などのバージョンは以下のとおりです。

「Nuxt ページング」「Nuxt ページネーション」とかで検索すると、Nuxt 3 じゃなくて Nuxt 2 の記事がたくさん出てきてわりと困りました。Nuxt 3 がリリースされてからもう 1 年半くらい経ってるはずですが、まだ Nuxt 2 を使ってるひと多いんだろうか…。

やりたいこと

以下の URL にアクセスした場合に、/pages ディレクトリに配置した共通の Vue ファイルが呼び出されるようにします。

  • /blog
  • /blog/page/2
  • /blog/page/3

実装方法

Nuxt のドキュメント にある「Dynamic Routes」の 2 重角括弧を使用します。ドキュメントによると /pages/[[slug]].vue//test の両方に一致する、つまり [[slug]] を任意にすることができます。

これを使用して、/pages/blog/[[slug]]/[[page]].vue というファイルを作成することで /blog/blog/page/2/blog/page/3 などに一致するようになります。

root/
 ├ pages/
 │ └ blog/
 │   └ [[slug]]
 │     └ [[page]].vue
 ├ package.json
 └ nuxt.config.ts

これで、一覧画面の 1 ページ目と 2 ページ目以降で共通の Vue ファイルを使用することができました。


ただ、このままだと /blog/hoge/blog/hoge/fuga といった関係のない URL でアクセスした場合も読まれてしまうので、別途関係のない URL の場合に 404 エラーとする、みたいな処理が必要になります。

/blog 以下にあるブログの投稿ページなどの URL がどのようになっているかにもよりますが、以下みたいな感じになると思います。

<script lang="ts" setup>
const route = useRoute()
const { data, error } = await useAsyncData(
  route.path,
  () => {
    // /blog もしくは /blog/page/<1~9から始まる数字>
    if (!/^\/blog(\/page\/[1-9]\d*)?\/?$/.test(route.path)) {
      throw new Error('URLの形式が不正です')
    }
    // 記事を取得する処理など
    return ...
  }
)

if (error.value) {
  throw createError({
    statusCode: 404,
    message: error.value.message,
    fatal: true,
  })
}
</script>

めちゃくちゃ余談ですけど、正規表現使うとき ChatGPT とか Copilot に質問すると代わりに考えてくれるので良いですね。「JavaScript で使える形式にして」みたいな指示もできるし。