Nuxt3でサイトマップを作ってみた!

みなさんこんにちは!
今回はNuxt3がそろそろ正式リリース(10/25)が近づいてきたので、
自作ブログリリースに向けて、サイトマップを作っていきたいと思います!

サイトマップ(sitemap.xml)とは

サイトマップを作ろうとしていたが、ふとこんな疑問を持ってしまった。
「なんでサイトマップ作るんだっけ?」
作った方がSEO的に良いという話は聞いたことがあるが、
そもそも論としてはわかっていなかった気がする。
なので、多少なりともサイトマップについて調べてみた。

Sitemaps(サイトマップ)標準は、ウェブマスターがサーチエンジンに、サイト内でクロールすべきURLを教えるための方式を規定するものである。サイトマップは、当該サイトにおけるURL全てをリストした、XMLファイルとして提供し、ウェブサイト運営者は各URLに付加的情報として、最終更新日時や更新頻度、他のURLとの相対的な重要度を加えたりできる。Sitemapsに対応したサーチエンジンでは、この情報を使って、サイトのクロールをより効率的に行えるようになる。サイトマップはサーチエンジンへのURL追加規約であり、URL排除規約であるrobots.txtを補完するものである。 https://ja.wikipedia.org/wiki/Sitemaps

なるほど!
GoolgeなどのサーチエンジンにクロールすべきURLを教えることも、サイトマップを設置する目的の一つらしいです。
確かに、「Google Search Console」を利用するときはサイトマップを登録しますね。
さらに「priority」タグを使うことで、URLの重要度も追加できるとのこと!
これは知らなかった。

Nuxt3でサイトマップを作る

サイトマップについて、軽く調査し終えたところで、
早速、サイトマップを作っていこう!
 
とりあえずNuxtでサイトマップといえば「nuxt/sitemap」だと思うが、
制作している現在は、まだサポートしていないみたいだった。
https://github.com/nuxt-community/sitemap-module/issues/246
 
なので、自作で作るしかないかな?と思いネットを漁っていたら下記のようなサイトを見つけた。
https://github.com/benoitdemaegdt/nuxt3-sitemap
 
何!?SSG作成時にも対応しているだと!
これは試すしかない!

「yarn generate」を2回打たないとsitemapが反映しなかった

こちらのサイトの「Setup for a dynamic site powered by @nuxt/content with prerendering」項目の手順を参考に実装したが、
1回目の「yarn generate」時には、サイトマップが作成されなかった。
なぜだろう?
 
理由は下記URLに記載のようでした。
https://github.com/benoitdemaegdt/nuxt3-sitemap/issues/9

どうやら、「.cache」フォルダ内サイトマップを作成していない状態で「yarn generate」を実行すると、
「.output/public」内にサイトマップが作成されないみたい。
2回目の「yarn generate」を実行すると、以前の「.cache」フォルダ内サイトマップが参照され、
「.output/public」内にサイトマップが作成されるみたいだな。

「yarn generate」を一回で済ませる方法の案

ではどうするか?
私は単純に「sitemap.ts」を、下記のようにコードを修正することで対応しました。


import { mkdirSync, writeFileSync, copyFile } from 'fs'
import { Readable } from 'stream'
import { dirname } from 'path'
import { SitemapStream, streamToPromise } from 'sitemap'
import { defineNuxtModule, createResolver } from '@nuxt/kit'

// https://github.com/benoitdemaegdt/nuxt3-sitemap
export default defineNuxtModule({
    meta: {
        name: 'sitemap',
        version: '0.0.1',
        configKey: 'sitemap',
        compatibility: { nuxt: '^3.0.0-rc.12' },
    },
    defaults: {
        hostname: 'http://localhost:3000',
    },
    async setup(options, nuxt) {
        async function generateSitemap(routes) {
            const sitemapRoutes = routes.map((route) => route.path)

            // https://github.com/ekalinin/sitemap.js#generate-a-one-time-sitemap-from-a-list-of-urls
            const stream = new SitemapStream({ hostname: options.hostname })
            return streamToPromise(Readable.from(sitemapRoutes).pipe(stream)).then(
                (data) => data.toString()
            )
        }

        function createSitemapFile(sitemap, filepath) {
            const dirPath = dirname(filepath)
            mkdirSync(dirPath, { recursive: true })
            writeFileSync(filepath, sitemap)
        }

        const resolver = createResolver(import.meta.url)
        const filePath = resolver.resolve(
            nuxt.options.srcDir,
            'node_modules/.cache/.sitemap/sitemap.xml'
        )

        nuxt.options.nitro.publicAssets = nuxt.options.nitro.publicAssets || []
        nuxt.options.nitro.publicAssets.push({
            baseURL: '/',
            dir: dirname(filePath),
        })

        nuxt.hook('nitro:build:before', (nitro) => {
            const paths = []
            const EXCLUDED_KEYWORDS = ['/api/_content', '_payload.js', '200.html']
            nitro.hooks.hook('prerender:route', (route) => {
                const shouldBeAddedToSitemap = EXCLUDED_KEYWORDS.every(
                    (excludedKeyword) => !route.route.includes(excludedKeyword)
                )
                if (shouldBeAddedToSitemap) {
                    paths.push({ path: route.route })
                }
            })
            nitro.hooks.hook('close', async () => {
                const sitemap = await generateSitemap(paths)
                createSitemapFile(sitemap, filePath)

                const outptFilepath = resolver.resolve(
                    nuxt.options.srcDir,
                    '.output/public/sitemap.xml'
                )

                copyFile(filePath, outptFilepath, (err) => {
                    if (err) {
                        console.log('sitemap.xml No created')
                        console.log(err)
                    } else {
                        console.log('sitemap.xml created')
                    }
                });
            })
        })
    },
})

 「copyFile」を使用し、「.output/public/sitemap.xml」のファイルを「node_modules/.cache/.sitemap/sitemap.xml」の内容に更新(上書きコピー)するように修正しただけです。
私が試したところ、修正後のコードでは、1回目の「yarn generate」時にも「.output/public/sitemap.xml」が作られるようになっていました!

まとめ

今回はNuxt3でサイトマップを作成する方法を紹介いたしました。
nuxt/sitemap」がNuxt3で使えれば使ったのですが、まだ対応していないようでしたので、
違うコードを利用しての実装となりました。
nuxt/sitemap」のNuxt3サポートが対応したら、書き換えようかなとは思っています。

それではまた次回の記事でお会いしましょう!

■関連記事(精度そんな良くないかもwww)

■関連トピック

2022 - Izanagi's Site