跳到正文
AS

你的 sitemap 迟早会漂移,所以别手写,去派生它

  • seo
  • nextjs
  • architecture

一份 sitemap,如果列着早已下线的页面,又漏掉刚刚上线的页面,那还不如不要。它只会教会搜索引擎不再信任这个文件。根本原因通常不是粗心,而是这份 sitemap 本身就是路由信息的第二份副本。

两份副本必然漂移

在 Next.js App Router 里新增一个页面,只需要建一个文件夹。这个动作和 sitemap 毫无关系。于是 sitemap 的正确性,完全取决于所有人是否都记得一个没有任何工具约束的步骤。

再过几个月,再来几位协作者,它一定会出错。

把路由只声明一次

正确的做法是:把路由写在一个地方,让其余部分都从这里读取。

// src/lib/seo/routes.ts
export const staticRoutes = [
  { path: "/", label: "Home", priority: 1.0, showInNav: false },
  { path: "/projects", label: "Projects", priority: 0.9, showInNav: true },
  { path: "/blog", label: "Writing", priority: 0.9, showInNav: true },
  { path: "/about", label: "About", priority: 0.8, showInNav: true },
] as const;

导航栏遍历它,页脚遍历它,sitemap.ts 也遍历它,再补上动态的 slug。至此,「这个站点到底有哪些页面」这个问题,只剩下唯一一份答案。

再用一个测试把闭环补上

派生 sitemap 消除了绝大部分漂移,但没有全部消除:仍然有人可能创建 app/(site)/speaking/page.tsx,却始终没把它加进注册表。一个测试就能抓住这种情况:

test("every route folder is registered", () => {
  const folders = readRouteFoldersFromDisk();
  const registered = staticRoutes.map((r) => r.path);
  expect(folders.sort()).toEqual(registered.sort());
});

从此,文件系统和注册表一旦不一致,构建必然变红。

更一般的形态

这个模式其实无关 sitemap。它想说的是:当同一个事实被写在两个地方时,就应该由工具从其中一个派生出另一个,或者断言两者一致。任何依赖人去记住的东西,都是一次潜伏的回归,只等着一个忙碌的星期把它引爆。