Nuxt.js の Sitemap Module を SSR 環境下で利用する
Nuxt.js の Sitemap Module について、何故か Generate による Static Page / Full Static でしか利用できないと思いこんでいたのでメモ。
Sitemap Module について
Nuxt.js オフィシャルに提供されているサイトマップの生成用モジュールであり、基本的にはモジュールとして読み込むだけで自動でサイトマップを生成してくれる。
基本的には $ yarn add @nuxtjs/sitemap
を行い、 nuxt.config.js
にて modules 指定を行うだけ。
https://www.npmjs.com/package/@nuxtjs/sitemap
サイトマップを必要とする多くのサイトで利用されており、大体 weekly download が 30k 程度と人気のモジュールとなっている。
対象となるエンドポイントのカスタマイズ
Sitemap Module には、オプションで nuxt.config.js
を拡張する形で sitemap: object[] | object | () => object | false | false
を受け取るような仕組みが用意されており、これを利用することで、サイトマップ生成に関する様々な拡張を行うことができる。
そんな Sitemap の項目の一つに routes
という項目があり、これは string | object
の配列を返す関数を定義することで、手動でサイトマップのリストを定義できる。
import { NuxtConfiguration } from '@nuxt/types'
const config: NuxtConfiguration = {
sitemap: {
hostname: 'https://example.com',
routes() {
return [
'/',
'/about',
'/contact',
]
}
}
}
export default config
こんな感じ。
基本的にはこれが本番を想定したビルドタイムでのみ処理されると考えていたが、あるタイミングから generate: Boolean
が追加されたり、今だと平然と generate 以外のシチュエーションが想定されている。
Sitemap Module の動き
Nuxt.js のファイル生成系のモジュールは大きく以下の2つにわけられる。
nuxt build
やnuxt generate
時に解決される静的出力serverMiddleware
を利用した動的出力
Sitemap Module は、 generate 時には 1 を、 SSR 時には 2 の戦略を自動的にスイッチしてくれる。
具体的には以下のようなコードが記述されている。
// https://github.com/nuxt-community/sitemap-module/blob/dev/lib/middleware.js
// Add server middleware for sitemapindex.xml
nuxtInstance.addServerMiddleware({
path: options.path,
handler(req, res, next) {
// Init sitemap index
const xml = createSitemapIndex(options, base, req)
// Check cache headers
if (validHttpCache(xml, options.etag, req, res)) {
return
}
// Send http response
res.setHeader('Content-Type', 'application/xml')
res.end(xml)
},
})
routes の動的出力
というわけで本題の routes を動的に生成について。これは基本的に Full Static 時に CMS などにアクセスする場合の routing 設定と同じようにすると OK。
例えば WordPress REST API にアクセスするシチュエーションを想定する。
ページネーションやエラーハンドリングなどは省略するが、一例として pages/_id.vue
には以下のような記述がある場合、
<template>
<section>
<h1></h1>
<div v-html="post.content.rendered">
</div>
</section>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
async asyncData({ app, params }) {
const [ post ] = await app.$axios.get(`/posts?slug=${params.slug}`)
return {
post
}
}
})
</script>
サイトマップの routes の定義は以下のようになる。
import axios from 'axios'
import { NuxtConfiguration } from '@nuxt/types'
const API_ROOT = 'https://example.com/wp-json/wp/v2'
const config: NuxtConfiguration = {
sitemap: {
hostname: 'https://example.com',
async routes() {
const apiClient = axios.create({
baseUrl: API_ROOT
})
const { data } = await apiClient.get('/posts/')
return data.map((post) => `/posts/${post.slug}`)
}
}
}
export default config
基本的には SSR 時の generate と同じであるが、 @nuxtjs/pwa
などとは違い、SSR モードの場合はビルドタイムではなくランタイムでこの中身が適切に処理される。
cacheTime のオプションも存在するため、生成にあたってある程度の負荷が予想されるエンドポイントであっても、キャッシュ設定を行っておけば問題ない。