NuxtでGeneratした時に手軽にsitemapを作る
前提
Nuxt(@2.15.8)で静的なサイトを制作しています。ヘッドレスCMSを使って各ページを生成する形のシンプルなサイトです。
今回は動的なページのリストページがあるので、動的なルートの生成は各ページからリンク(NuxtLink)を辿って、Nuxtがいい感じに生成してくれます。
使用技術
- フレームワーク:Nuxt.js(@2.15.8)
- モジュール: nuxtjs/sitemap(@2.4.0)
事前確認
@nuxtjs/sitemapを導入するにあたり、調べてみたとろこ、動的なページの場合、nuxt.config.jsでaxios
してroutesを生成する、という記事が出てきました。
例:axiosを使ってroutesを返す
export default {
modules: [
'@nuxtjs/sitemap'
],
sitemap: {
hostname: 'https://example.com',
exclude: [
'/company'
],
routes: async () => {
// APIから動的なルートの情報を取得
const res = await axios.get(
process.env.API_URL,
{
headers: {
'X-API-KEY': process.env.API_KEY
}
}
)
// 動的なルートを配列として返す
return res.data.contents.map((info) => `/info/${info.id}`)
}
}
}
今回の場合の懸念
ブログの様なサイトであれば上記の形でも問題ないと思いますが、今回のサイトではカスタムのCMSということもあり、取得したデータを編集してページを生成しています。
ですので、出来ればNuxtのルーティングをそのまま使えないかと思いました。何か修正した時にも修正箇所が複数、それも別に実装されているのは良い状態では無いと考えました。
また、個別の理由ですが、nuxt.config.jsでfetch
を使えなかったので、別のやり方を探し始めました。
解決策を探してみる
何か良い方法は無いかと探していたら下記のサイトを見つけました。
ポイント
実務でもNuxtを使っているのですが、hook
というのを初めて見ました。
コード全体(上記より引用)
import { Module } from '@nuxt/types'
const generator: Module = function () {
this.nuxt.hook('generate:done', async (context: any) => {
const routesToExclude: string[] =
process.env.NUXT_ENV_EXCLUDE_ROUTES
? process.env.NUXT_ENV_EXCLUDE_ROUTES.split(',') : []
const allRoutes: string[] = await Array.from(context.generatedRoutes)
const routes: string[] = await allRoutes.filter((route: string) => !routesToExclude.includes(route))
this.nuxt.options.sitemap.routes = await [...routes]
})
}
export default generator
hookについて調べると下記のサイトが参考になりました。
実装してみる
先程のコードを今回のプロジェクトにコピペしてみました。
modluesフォルダにgenerator.ts
を作ってコードにconsole.logを使って何をしているか確認してみました。context
にはgenerateの情報やNuxt自身の情報などほぼ全ての情報が格納されていました。そのなかでcontext.generatedRoutes
には生成されたルートが格納されていました。
整理すると下記の用になりました
import { Module } from '@nuxt/types'
const generator: Module = function () {
// generateの完了後に呼ばれるフック
this.nuxt.hook('generate:done', async (context: any) => {
//
const routes = []
// 生成されたルート(Set)からルートを取得
for (const route of context.generatedRoutes) {
// routesにパスを追加
routes.push({ url: route })
}
// サイトマップのroutesに追加
this.nuxt.options.sitemap.routes = routes
})
}
export default generator
考え方としてはとてもシンプルで、generateされたルートを@nuxtjs/sitemapのroutes
に設定しているだけです。他の記事であったような、axiosを使った場合との違いは、APIで取得する部分をNuxtが生成したルートを使っている点だけです。
モジュールが必須なのか
先程のサイトだとモジュール化してbuildModules
で読み込んでいます。ただ、内容的にモジュール化するほどでは無いので、hook
の仕様を確認してnuxt.config.jsに記述してみました。
下記の書き方でも問題なく動作しました。違いとしてはnuxt
へのアクセスをcontext
を通して行っている点です。(ただ、モジュールの方も同じアクセスは可能だと思います)
export default {
// sitemapの設定
sitemap: {
hostname: 'https://example.com',
exclude: [
'/company',
],
},
// hook
hooks: {
generate: {
done(context) {
// generate後にサイトマップに使う動的ルートを取得して、サイトマップに追加する
const routes = []
for (const route of context.generatedRoutes) {
// 除外以外を処理 (除外はsitemapの方で指定可能)
if (!context.nuxt.options.sitemap.exclude.includes(route)) {
routes.push({
url: route
})
}
}
// サイトマップのroutesに追加
context.nuxt.options.sitemap.routes = routes
}
}
},
}
まとめ
動的なルートからSitemapを生成する方法だと、axiosを使った記事が多かったので、今回の方法を知れてよかったと思っています。
ただ、Nuxt3ではまた違ってくると思うので、使える期間は短いかも知れません。
また、今回のプロジェクトの場合だと問題になりませんでしたが、SitemapのlastMod
が今回の方法だと一捻り必要そうだなと思いました。
Discussion