SEO

使用查询字符串的 SEO 陷阱和最佳实践

如果您的页面使用查询字符串仅用于本地状态,您应该为页面添加一个规范 URL,以告知 SEO 爬虫忽略查询字符串并在没有它的情况下索引页面。

在 Next.js app router 中,这是通过 metadata 对象完成的:

import type { Metadata } from 'next'

export const metadata: Metadata = {
  alternates: {
    canonical: '/url/path/without/querystring'
  }
}

然而,如果查询字符串定义了页面显示的内容(例如:YouTube 的 watch URL,如 https://www.youtube.com/watch?v=dQw4w9WgXcQ),您的规范 URL 应该包含相关的查询字符串,并且您仍然可以使用您的解析器来读取它,并序列化规范 URL。

/app/watch/page.tsx
import type { Metadata, ResolvingMetadata } from 'next'
import { notFound } from "next/navigation";
import {
  createParser,
  parseAsString,
  createLoader,
  createSerializer,
  type SearchParams,
  type UrlKeys
} from 'nuqs/server'

const youTubeVideoIdRegex = /^[^"&?\/\s]{11}$/i
const youTubeSearchParams = {
  videoId: createParser({
    parse(query) {
      if (!youTubeVideoIdRegex.test(query)) {
        return null
      }
      return query
    },
    serialize(videoId) {
      return videoId
    }
  })
}
const youTubeUrlKeys: UrlKeys<typeof youTubeSearchParams> = {
  videoId: 'v'
}
const loadYouTubeSearchParams = createLoader(
  youTubeSearchParams,
  {
    urlKeys: youTubeUrlKeys
  }
)
const serializeYouTubeSearchParams = createSerializer(
  youTubeSearchParams,
  {
    urlKeys: youTubeUrlKeys
  }
)

// --

type Props = {
  searchParams: Promise<SearchParams>
}

export async function generateMetadata({
  searchParams
}: Props): Promise<Metadata> {
  const { videoId } = await loadYouTubeSearchParams(searchParams)
  if (!videoId) {
    notFound()
  }
  return {
    alternates: {
      canonical: serializeYouTubeSearchParams('/watch', { videoId })
      // /watch?v=dQw4w9WgXcQ
    }
  }
}