Nuxt.jsmicroCMS

Nuxt3 microCMSブログでfull static generationする

公開日:2023-06-18 更新日:2023-06-18

今回はNuxt3 + microCMS構成でデプロイ時にfull static generationする方法についてまとめました。

Nuxt.jsの公式ドキュメントでは以下のページが該当します。

https://nuxt.com/docs/getting-started/deployment#static-hosting

自動でやる場合

公式ドキュメントでは、以下のようにnuxt.config.tsに記述することで、Nuxt側でよしなにリンクを生成してくれるとあります。

// nuxt.config.ts
defineNuxtConfig({
  nitro: {
    prerender: {
      crawlLinks: true
    }
  }
})

私も当初はこの方法を採用していました。

しかし、まだ安定的な動作をしていないのか、私の構成に問題があるのか、デプロイ時に謎のwarningが発生していました。

[nitro 19:21:26]  WARN  Prerendering long route "/"%7B%%20url%20'kakeibo:payment_list'%20%%7D&..." (1606) can cause filesystem issues since it exceeds 953-character limit when writing to

詳細は不明ですが、microCMSで書いたブログ本文のURLっぽい箇所ををNuxtが検知してしまって、バグってるような印象を受けます。

一応、記事自体のgenerateはできているみたいですが、少し気持ち悪いです。

なので、crawlLinks:trueとせずに、ルートを動的に追加する方法に切り替えました。

マニュアルでルートを追加する方法

Nuxt3の公式ドキュメントを辿ると手動で追加する方法として、以下のコードが載っています。

// nuxt.config.ts
defineNuxtConfig({
  nitro: {
    prerender: {
      routes: ['/user/1', '/user/2']
    }
  }
})

ここのroutesの部分に追加したパスがgenerateされる仕組みです。

理論上は全てのパスを記述すればgenerateされます。しかし、記事が増えた場合にその都度、記述を追加しなければなりません。

記事はmicroCMSで作成しているので、APIリクエストを投げて動的にルートを作るのが現実的な要件となります。

動的にルートを生成する

リポジトリのissueで議論がされていました。

https://github.com/nuxt/nuxt/issues/13949#issuecomment-1397322945

まだ実験的な部分もあり、変更の可能性もあるかもしれませんが、現状は以下のようにして、動的ルートを作るようです。

// nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    async 'nitro:config' (nitroConfig) {
      if (nitroConfig.dev) { return }
      // ..Async logic..
      nitroConfig.prerender.routes.push('/custom')
    }
  }
})

あとは// ..Async logic.. の部分を処理していけばよさそうです。

ブログのページ構成について

私のブログは記事に複数のタグをつけるという構成にしています。

そのため、追加するべきURLは以下の三種類になります。

記事詳細ページルート:
/<slug>
通常のページング用のルート:
/page/<p>
タグ絞り込みをした場合のページングルート:
/tags/<tagId>/page/<p>

ルート生成の考え方

まず記事詳細ページは記事のidを全部取得して、ルートに追加すればいいです。

次に、ページング用のルートです。

私は1ページあたりの記事数は10に設定しています。

そのため、全部の記事数が25だったら、以下のようなルートを追加する必要があります。

/page/1
/page/2
/page/3

これも合計記事数を取得して10で割るという考え方でいいでしょう。

最後にタグごとのページングが少し厄介でしょうか。

例えば25の記事のうちPythonタグがついている記事が15個、TypeScriptタグが付いている記事が5個あった場合。

/tags/python/page/1
/tags/python/page/2
/tags/typescript/page/1

こういうイメージですね。

タグごとの記事数を取得する必要があるのですが、microCMSのAPIでは被参照コンテンツの数はかえってこないです。

なので、タグの一覧を取得してタグごとにAPIリクエストをする方法が一般的です。

しかし、私は記事一覧の全データを取得して、記事数をタグごとに数え上げる方法を取りました。

このあたりは以前のエントリ「microCMS APIで被参照の数を取得する」にも書きました。

私のブログ記事数は現在100件程度で、タグは多くて3つです。なので、高々300回程度の計算量で数え上げることができます。

厳密な比較はしていませんが、APIリクエストを都度なげるのと大きなパフォーマンスの違いは出ないと判断しました。

実装

nuxt.config.tsの実装は以下のようになりました。

microcms-js-sdkを使っています。

// nuxt.config.ts
const { API_KEY, SERVICE_DOMAIN, GA_ADSENSE_ID } = process.env;
import { createClient } from 'microcms-js-sdk'; //ES6

export default defineNuxtConfig({
    ssr: true,
    runtimeConfig: {
        apiKey: API_KEY,
        serviceDomain: SERVICE_DOMAIN
    },
    hooks: {
        async "nitro:config"(nitroConfig) {
            if (nitroConfig.dev) {
                return;
            }
            // falseにしてマニュアルで追加する
            nitroConfig.prerender.crawlLinks = false
            // 1ページ当たりの記事数
            const limit = 10
            // microCMSクライアントをインスタンス化する
            const client = createClient({
                serviceDomain: SERVICE_DOMAIN,
                apiKey: API_KEY,
            })
            // 記事数を取得するために一度リクエスト
            const data = await client.getList(
                {
                    endpoint: 'post',
                    queries: {
                        limit: 0,
                        fields: 'id'
                    }
                }
            )
            const totalCount = data.totalCount
            // limitを記事数にしてリクエスト
            const allPosts = await client.getList(
                {
                    endpoint: 'post',
                    queries: {
                        limit: totalCount,
                        // idとtagを指定する
                        fields: 'id,tag'
                    }
                }
            )
            // タグに紐づいている記事の数
            const tagCount: Record<string, number> = {}
            // 記事を繰り返す
            for (const elm of allPosts.contents) {
                const slug = elm.id
                const tags = elm.tag
                // タグに紐づいた記事の数をカウントアップ
                for (const tag of tags) {
                    if (tagCount[tag.id]) {
                        tagCount[tag.id]++
                    } else {
                        tagCount[tag.id] = 1
                    }
                }
                // 記事詳細をルートに加える
                nitroConfig.prerender.routes.push(`/${slug}`)
            }
            // ページ数をルートに加える
            const pageCount = Math.ceil(totalCount / limit)
            for (let p = 1; p < pageCount + 1; p++) {
                nitroConfig.prerender.routes.push(`/page/${p}`)
            }
            // タグごとにページ数を計算してルートに加える
            for (const tagId in tagCount) {
                const cnt = tagCount[tagId]
                const tagPageCount = Math.ceil(cnt / limit)
                for (let p = 1; p < tagPageCount + 1; p++) {
                    nitroConfig.prerender.routes.push(`/tags/${tagId}/page/${p}`)
                }
            }
        },
    },
})

generate時のwarningが消えたのと、crawlLinks:trueで自動生成するよりも高速に動いている感じがあります。

ソースコード

ブログのソースコードは全文githubに公開しているので、適宜参照お願いします。

https://github.com/qlitre/qlitre-weblog-nuxt3

Twitter Share