※記事のURLの一部は広告を含みます。

画像のうち一部が正しく表示されていない期間がありました。ご迷惑をおかけし申し訳ありません。

Next.jsで静的なXMLサイトマップを作る【外部ライブラリ不要】の絵文字

Next.jsで静的なXMLサイトマップを作る【外部ライブラリ不要】

2022-04-24

2022-04-24

はじめに

Next.js 廃れないでほしいこふです。

動的なサイト(TwitterZenn など)ではない静的なサイトにおいて SSGStatic Site Generation)時にスクリプトを動かしてサイトマップを作ります。

用途は、以下のような不規則にページが増えないサイトです。

  • 個人・企業のブログ
  • 個人の日記サイト

環境

Next.js 以外の外部ライブラリは用いません。例えばnextjs-sitemapというライブラリなど。

1node -v 2v16.14.0
package.json
1{ 2 "dependencies": { 3 ... 4 "react": "^17.0.2", 5 "react-dom": "^17.0.2", 6 "next": "^12.1.5", // getStaticPropsが使えるならOK 7 ... 8 }, 9 "devDependencies": { 10 ... 11 "typescript": "4.5.4" 12 ... 13 } 14}

方針

関数を作ってサイトマップの文字列を作成し、ファイルとして書き込む、です。

そのスクリプトは Next.jsgetStaticProps で動かし、pages/sitemap.tsx という場所で作成します。


自分の考えるメリット

  • ユーザーが見るサイトマップを作る準備ができる
  • package.json を変更しなくて良い
  • Next.jsAPI Routesを使用しない(Vercel でなくても使える!)

デメリット

  • 動的サイトには使えない

ソースコード

src/pages/sitemap.tsx
1import { generateSitemapXml } from '@/libs/sitemap' // 下で出てきます 2import { NextPage } from 'next' 3import DefaultErrorPage from 'next/error' 4import Head from 'next/head' 5 6const Page: NextPage = () => { 7 return ( 8 <> 9 <Head> 10 <meta name="robots" content="noindex" /> 11 </Head> 12 <DefaultErrorPage statusCode={404} /> 13 {/* 自分でエラー用のコンポーネント使う */} 14 </> 15 ) 16} 17 18export default Page 19 20export const getStaticProps = async () => { 21 await generateSitemapXml() 22 return { props: {} } 23}
src/lib/sitemap.ts
1import { abouts } from '@/constants/footer' 2import { FrontURL } from '@/constants/urls' 3import fs from 'fs' 4 5export const generateSitemapXml = async () => { 6 let xml = `<?xml version="1.0" encoding="UTF-8"?>` 7 xml += `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">` 8 const nowJST = new Date( 9 Date.now() + (new Date().getTimezoneOffset() + 9 * 60) * 60 * 1000 10 ) 11 const updatedDate = nowJST.toISOString().split('T')[0] 12 13 // about site 14 abouts.forEach((p) => { 15 xml += ` 16 <url> 17 <loc>${FrontURL}${p.href}</loc> 18 <updatedDate>${updatedDate}</updatedDate> 19 <changefreq>monthly</changefreq> 20 </url> 21 ` 22 }) 23 24 // legal 25 ... 26 27 // contact 28 ... 29 30 // blog 31 ... 32 33 xml += `</urlset>` 34 fs.writeFileSync('public/sitemap.xml', xml) 35 return null 36}

ソースコードの解説

sitemap.tsx

1import { generateSitemapXml } from '@/libs/sitemap' // 下で出てきます 2import { NextPage } from 'next' 3import DefaultErrorPage from 'next/error' 4import Head from 'next/head'

必要なライブラリをインポートします。

1const Page: NextPage = () => { 2 return ( 3 <> 4 <Head> 5 <meta name="robots" content="noindex" /> 6 </Head> 7 <DefaultErrorPage statusCode={404} /> 8 {/* 自分でエラー用のコンポーネント使う */} 9 </> 10 ) 11}

大事な点が 2 つあります。

  • クローラーに index させない
  • 404 エラーのページを見せる(ユーザーのため)

ユーザーが見るサイトマップを作らないなら、です。

もしユーザーも見るサイトマップページ(WordPress でよくあるやつ)を作るなら、index させ、エラーではなく表示したいコンポーネントを作れば良いだけです。

1export default Page 2 3export const getStaticProps = async () => { 4 await generateSitemapXml() 5 return { props: {} } 6}

ポイントは getStaticProps でサイトマップを作る関数である generateSitemapXml を呼び出すということ。

そうすることで package.json などでコードを追加する必要なく関数をビルド時に一回だけ呼び出すことができます。

sitemap.ts

src/lib/sitemap.ts
1import { abouts } from '@/constants/footer' 2import { FrontURL } from '@/constants/urls' 3import fs from 'fs'

必要なライブラリ、定数をインポートします。

1export const generateSitemapXml = async () => { 2 let xml = `<?xml version="1.0" encoding="UTF-8"?>` 3 xml += `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">` 4 const nowJST = new Date( 5 Date.now() + (new Date().getTimezoneOffset() + 9 * 60) * 60 * 1000 6 ) 7 const updatedDate = nowJST.toISOString().split('T')[0] 8 9 // about site 10 abouts.forEach((p) => { 11 xml += ` 12 <url> 13 <loc>${FrontURL}${p.href}</loc> 14 <updatedDate>${updatedDate}</updatedDate> 15 <changefreq>monthly</changefreq> 16 </url> 17 ` 18 }) 19 20 // legal 21 ... 22 23 // contact 24 ... 25 26 // blog 27 ... 28 29 xml += `</urlset>` 30 fs.writeFileSync('public/sitemap.xml', xml) 31 return null 32}

関数本体です、少し長いですがやってることはシンプルです。

ポイントは以下の通り

  • xml の構造にあるように文字列を加算する
  • fs を使ってファイルとして書き込む
  • public 以下に配置する(だれでもアクセスできる場所)

updatedDate ですが、最後に編集した日付としてビルド時を指定しています。

ブログであれば、その情報に紐づいた日付を updatedDate にすれば OK です。


余談ですが、abouts という変数は別のディレクトリからインポートしています。

どんなやり方であれ、ページのリンクを取得して生成する仕組みがあれば問題ありません。

自分は、値を一箇所で管理するようにしているので、表示名(label)やリンク先のパス(href)も別の場所で管理しています。(hrefよりpathの方が良いかも。。。)

1export const abouts: LinksType = [ 2 { 3 label: AboutName, 4 href: AboutPath, 5 }, 6 { 7 label: FaqsName, 8 href: FaqsPath, 9 }, 10 { 11 label: ChangelogName, 12 href: ChangelogPath, 13 }, 14 { 15 label: LoadmapName, 16 href: LoadmapPath, 17 }, 18]

robots.txt

以下のようにクローラーにわかってもらえるよう指定します。

robots.txt
1Sitemap: https://example.com/sitemap.xml

おわりに

これでブログ・日記サイトをクローラーに認知してもらう仕組みができました。

src/pages/sitemap.xml に人間が見るサイトマップを作る時の手間を減らせるのでおすすめです。

これをGoogle Search Consoleなどで送信してあげれば OK!

参考




共有する