microCMS

NuxtのJamstack構成におけるページングの実装

エンジニアリング
2020/08/11 柴田 和祈

Nuxt.jsでJamstackなサイトを構築する際にハマりがちなページングについて解説します。
こちらの記事のコードにページング機能を追加していきたいと思います。

microCMS + NuxtでJamstackブログを作ってみよう
https://microcms.io/blog/microcms-nuxt-jamstack-blog

一覧画面のページング

前回の記事では以下のような構成で記事の一覧画面と詳細画面を作成しました。

========
pages/
 index.vue
 _slug/
  index.vue
========

ページング時は/page/2のようにアクセスしてもらうため、pages/page/_p/index.vueのように新しくページング用テンプレートを用意したくなりますが、ページングのUIレイアウトは記事一覧画面と基本的には同じです。
なので、テンプレートとしては記事一覧画面(pages/index.vue)をページング画面でも利用する形で開発を進めます。
pages/index.vueに修正を加えていきます。

// pages/index.vue

<template>
  <ul>
    <li v-for="content in contents" :key="content.id">
      <nuxt-link :to="`/${content.id}`">{{ content.title }}</nuxt-link>
    </li>
  </ul>
</template>
<script>
import axios from 'axios'
export default {
  async asyncData({ params }) {
    const page = params.p || '1'
    const limit = 10
    const { data } = await axios.get(
      `https://your-service-id.microcms.io/api/v1/blog?limit=${limit}&offset=${(page - 1) * limit}`,
      { headers: { 'X-API-KEY': 'your-api-key' } }
    )
    return data
  }
}
</script>

URLのparams経由でページ情報を受け取り、microCMSのリクエストパラメータに含めることでページングを行います。

次に、nuxt.config.jsにてページング用のルーティングを設定していきます。

// nuxt.config.js

export default {
  // 略
  router: {
    extendRoutes(routes, resolve) {
      routes.push({
        path: '/page/:p',
        component: resolve(__dirname, 'pages/index.vue'),
        name: 'page',
      })
    },
  },
}

上記のように設定することで、URLから/page/:pにアクセスしてきた場合はpages/index.vueがテンプレートとして使用されます。
:pにあたる部分がpages/index.vueasyncData()において、params.pで取得可能です。
この時点でnpm run devによる開発環境上では、/page/2とアクセスすると2ページ目が表示されるようになっています。

加えて、Jamstack構成ではページング用のHTMLも事前に生成しておく必要があります。
こちらもnuxt.config.jsで設定します。

// nuxt.config.js

export default {
  // 略
  generate: {
    async routes() {
      const limit = 10
      const range = (start, end) =>
        [...Array(end - start + 1)].map((_, i) => start + i)

      // 一覧のページング
      const pages = await axios
        .get(`https://your-service-id.microcms.io/api/v1/blog?limit=0`, {
          headers: { 'X-API-KEY': API_KEY },
        })
        .then((res) =>
          range(1, Math.ceil(res.data.totalCount / limit)).map((p) => ({
            route: `/page/${p}`,
          }))
        )
      return pages
    },
  },
}


上記の設定をしておくことで、nuxt generate時にページング用のHTMLが生成されます。
ここではtotalCountだけ取得できればページングのためのルーティングは計算できるので、limit=0として最小限のレスポンスに留めています。
また、以前は動的なルーティングパスに関しては全てこちらに指定してあげる必要がありましたが、最近はNuxt側で内部的なクロールによって動的なルーティングパスを検知してくれるようになったので、最小限の指定で済むようになりました。

以上で、記事一覧のページング用HTMLも静的生成できるようになりました。

応用編:カテゴリー別のページング

記事をカテゴリー別に一覧表示し、ページングも行うパターンを考えてみましょう。
やることは同じですが、カテゴリーによるフィルタリングが必要なので複雑になります。

前提として、カテゴリーAPIを事前に作成し、ブログAPIからコンテンツ参照しておく必要があります。

microCMS + NuxtでJamstackブログを作ってみよう
https://microcms.io/blog/microcms-nuxt-jamstack-blog

上記の記事の「9. カテゴリーを追加する」を参考にしてみてください。

まずはテンプレートです。
先ほどと同じく、記事一覧画面(pages/index.vue)を流用します。

// pages/index.vue

<template>
  <ul>
    <li v-for="content in contents" :key="content.id">
      <nuxt-link :to="`/${content.id}`">{{ content.title }}</nuxt-link>
    </li>
  </ul>
</template>
<script>
import axios from 'axios'
export default {
  async asyncData({ params }) {
    const page = params.p || '1'
    const categoryId = params.categoryId
    const limit = 10
    const { data } = await axios.get(
      `https://your-service-id.microcms.io/api/v1/blog?limit=${limit}${
        categoryId === undefined ? '' : `&filters=category[equals]${categoryId}`
      }&offset=${(page - 1) * limit}`,
      { headers: { 'X-API-KEY': 'your-api-key' } }
    )
    return data
  }
}
</script>

URLのパラメータからカテゴリーIDとページ情報を受け取り、microCMSにAPIリクエストします。
カテゴリーIDが指定してある場合のみフィルタリングを行い、指定されていない場合は通常の記事一覧のページングを行います。

次に、nuxt.config.jsでルーティングの設定です。

// nuxt.config.js

export default {
  // 略
  router: {
    extendRoutes(routes, resolve) {
      routes.push({
        path: '/page/:p',
        component: resolve(__dirname, 'pages/index.vue'),
        name: 'page',
      });
      routes.push({
        path: '/category/:categoryId/page/:p',
        component: resolve(__dirname, 'pages/index.vue'),
        name: 'category',
      })
    },
  },
}

これで、/category/hoge/page/2とアクセスすればidhogeであるカテゴリーの2ページ目が表示されるようになります。

次に静的生成対応のため、generateの設定を行います。

// nuxt.config.js

export default {
  // 略
  generate: {
    async routes() {
      const limit = 10
      const range = (start, end) =>
        [...Array(end - start + 1)].map((_, i) => start + i)

      // 一覧のページング
      const pages = await axios
        .get(`https://your-service-id.microcms.io/api/v1/blog?limit=0`, {
          headers: { 'X-API-KEY': 'your-api-key' },
        })
          .then((res) =>
            range(1, Math.ceil(res.data.totalCount / limit)).map((p) => ({
              route: `/page/${p}`,
            }))
          )

      const categories = await axios
        .get(`https://your-service-id.microcms.io/api/v1/categories?fields=id`, {
          headers: { 'X-API-KEY': 'your-api-key' },
        })
          .then(({ data }) => {
            return data.contents.map((content) => content.id)
          });

      // カテゴリーページのページング
      const categoryPages = await Promise.all(
        categories.map((category) =>
          axios.get(
            `https://your-service-id.microcms.io/api/v1/blog?limit=0&filters=category[equals]${category}`,
            { headers: { 'X-API-KEY': 'your-api-key' } }
          )
            .then((res) =>
              range(1, Math.ceil(res.data.totalCount / 10)).map((p) => ({
                route: `/category/${category}/page/${p}`,
              })))
      )
      )

      // 2次元配列になってるのでフラットにする
      const flattenCategoryPages = [].concat.apply([], categoryPages)
      return [...pages, ...flattenCategoryPages]
    },
  },
}


ちょっと複雑になってしまいましたが、以下のことを行なっております。

  1. カテゴリーの一覧を取得し、カテゴリーIDの配列を生成する
  2. カテゴリーごとに記事一覧を取得し、件数を取得してルーティングを作成する


以上で、静的生成時にカテゴリー別のページング用HTMLも生成されるようになります。

ページングのためのリンクはよしなに作成してください。
本ブログでいうとこんな感じのやつですね。

おわりに

NuxtのJamstack構成におけるページングの実装について解説しました。
一覧ページと個々のページについてはJamstack化できたけど、ページングがよくわからない!という方の助けになれば幸いです。

-----

microCMSは日々改善を進めています。
ご意見・ご要望は管理画面右下のチャット、公式Twitterメールからお気軽にご連絡ください!
引き続きmicroCMSをよろしくお願いいたします!

ABOUT ME

柴田 和祈
microCMSのデザイン、フロントエンド担当 / ex Yahoo / 2児の父 / 著書「React入門 React・Reduxの導入からサーバサイドレンダリングによるUXの向上まで 」 / Jamstack

microCMSとは

  1. 開発者、編集者どちらも分かりやすい管理画面

  2. 細かな権限管理や豊富な外部サービス・データ連携

  3. 安心の日本製・日本語でのチャットサポート