今回は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に公開しているので、適宜参照お願いします。