Sitemap 是提供搜尋引擎讀取站內頁面清單的常見方式,可以加速搜尋引擎幫我們的網站建立索引,而網站主需要做的事情就是在根目錄提供 檔案,檔案內容也需遵循 Sitemap 規範。
在 Next.js 的專案中,我們可以手動建立 、手動填入 xml,並以靜態檔案的方式部署。然而,每當我們的部落格新增或刪除文章時,就必須要手動更新 ,這顯然會造成管理上的困擾。為了解決這個問題,我的做法是取巧地建立一個空白頁面,但是在 裡渲染並輸出 :
import { writeToPublic } from '../utils/output'import { getPosts } from '../utils/post'import { generateSiteMapInXML } from '../utils/seo'
export default () => {}
export const getStaticProps = async () => { const posts = getPosts() const sitemap = generateSiteMapInXML(posts) writeToPublic('sitemap.xml', sitemap) return { props: {}, }}
此處獨立出 Utility function ,負責把檔案寫入 資料夾:
import fs from 'fs'import path from 'path'
export const writeToPublic = (relativeFilePath, fileContent) => { const absoluteFilePath = path.join(process.cwd(), 'public', relativeFilePath) fs.writeFileSync(absoluteFilePath, fileContent)}
Sitemap 檔案內容的渲染過程也獨立於 utility function :
export const generateSiteMapInXML = (posts) => { return `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> ${posts .map( (post) => ` <url> <loc>{`https://example.com/blog/${post.slug}`}</loc> <lastmod>${post.lastModifiedTime}</lastmod> </url>` ) .join('')}</urlset>`}
最後記得把這一份建置檔案列入 gitignore:
/public/sitemap.xml
要讓搜尋引擎能夠理解我們的網站內容,除了使用 Sitemap 編列頁面索引以外,每一個獨立頁面也都要能夠支援伺服器端渲染(SSR),這樣子爬蟲程式讀取頁面時才能夠拿到結構化的 Html 及頁面內容,而不是拿到需要被 Evaluate 的 Javascript。不過,Next.js 原生就已經支援 SSR 了,我們不必做任何處理就能享有 SSR 的效果,SSG 的過程也可以直接使用 SSR 產生的內容。
雖然對於爬蟲程式而言我們不用做任何努力,但是部落格的佈景主題、客製化元件等都是延遲載入 CSS,對於真人閱讀者而言,將會先看到 SSR 產生的 HTML,等到 CSS 被載入之後才套用設計過的佈景主題,這段載入 CSS 的時間差會造成閱讀者看到頁面閃爍、抖動、跳躍而影響閱讀體驗,為了讀者的眼睛著想,我的解法是把 CSS 納入 SSR 的過程當中。
由於我的 Design System 和各種調整樣式的處理方式都是透過 來完成,所以只要替 styled-components 設定 SSR 即可,參考 How to Set up Styled-Components with SSR in NextJS (Typescript)open_in_new 一文之後,兩個步驟就能搞定:
調整 Next Config
const nextConfig = { // ... compiler: { styledComponents: true, },}
export default withMDX(nextConfig)
更新
import Document, { Head, Html, Main, NextScript } from 'next/document'import { ServerStyleSheet } from 'styled-components'
class MyDocument extends Document { static async getInitialProps(ctx) { const sheet = new ServerStyleSheet() const originalRenderPage = ctx.renderPage try { ctx.renderPage = () => originalRenderPage({ enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />), //gets the styles from all the components inside <App> }) const initialProps = await Document.getInitialProps(ctx) return { ...initialProps, styles: ( <> {initialProps.styles} {sheet.getStyleElement()} </> ), } } finally { sheet.seal() } }
render() { return ( <Html> <Head /> <body> <Main /> <NextScript /> </body> </Html> ) }}
export default MyDocument
JSON-LD 是一種結構化語法,用來描述網站的型態及內容,搜尋引擎也會針對帶有 JSON-LD 的網頁呈現特化的搜尋結果排版。使用方式其實就是在網頁裡插入 標籤,而內容只要依照 Schema.orgopen_in_new 的規範填寫即可。
我的實作是撰寫專用的 SEO 元件,並且在文章頁面插入該元件:
import Head from 'next/head'import { generateJsonLdForPost } from '../../utils/seo'
export default ({ post }) => { return ( <Head> <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: generateJsonLdForPost(post), }} key="product-jsonld" /> </Head> )}
並且搭配 utility function 來產生 JSON-LD 內容:
export const generateJsonLdForPost = (post) => { const { HOST, SITE_NAME, AUTHOR_NAME } = getConfig() const jsonLd = { '@context': 'https://schema.org', '@type': 'BlogPosting', headline: post.frontMatter.title, genre: ['SEO', 'JSON-LD'], keywords: post.frontMatter?.keywords || [], url: getUrl(HOST, post), datePublished: post.frontMatter.publishDate, dateCreated: post.frontMatter.publishDate, dateModified: post.lastModifiedTime, articleBody: post.content, publisher: { '@type': 'Organization', name: SITE_NAME, url: HOST, logo: { '@type': 'ImageObject', url: `${HOST}/site/favicon.svg`, }, }, author: { '@type': 'Person', name: AUTHOR_NAME, url: HOST, }, creator: { '@type': 'Person', name: AUTHOR_NAME, }, mainEntityOfPage: { '@type': 'WebPage', '@id': `${HOST}/blog`, }, } if (post.frontMatter.hero) { jsonLd.image = `${HOST}${post.frontMatter.hero.src}` } if (post.frontMatter.excerpt) { jsonLd.description = post.frontMatter.excerpt } return JSON.stringify(jsonLd)}
如果想要驗證自己的 JSON-LD 是否正確,可以參閱 Google 的文件「測試結構化資料open_in_new」。