修复:Qwik 生成空的 sitemap.xml

发布: (2025年12月2日 GMT+8 13:50)
6 min read
原文: Dev.to

Source: Dev.to

问题

在使用 Node.js 适配器(nodeServerAdapter)部署 Qwik(或 Qwik City)应用时,sitemap.xml 在本地可以正常工作,但在生产环境中为空:

<!-- empty sitemap -->
  • 本地构建: ✅ 正常
  • 开发服务器: ✅ 正确提供 sitemap
  • 生产构建: ❌ sitemap 为空,且没有警告

Node.js 适配器在构建期间会运行 静态站点生成(SSG),即使是 SSR 构建。此步骤会:

  1. 扫描可静态导出的路由
  2. 根据找到的路由生成 sitemap
  3. 覆盖你手动放置的 public/sitemap.xml

如果 SSG 步骤找不到任何可导出的路由(例如动态路由、仅 SSR 的路由,或使用 Node.js 适配器时),就会生成一个空的 sitemap。


快速修复 – 禁用 SSG

对于纯 SSR 应用,你可以在适配器配置中彻底关闭 SSG。

adapters/node-server/vite.config.ts

import { nodeServerAdapter } from "@builder.io/qwik-city/adapters/node-server/vite";
import { extendConfig } from "@builder.io/qwik-city/vite";
import baseConfig from "../../vite.config";

export default extendConfig(baseConfig, () => ({
  build: {
    ssr: true,
    rollupOptions: {
      input: ["src/entry.express.tsx", "@qwik-city-plan"],
    },
  },
  plugins: [
    nodeServerAdapter({
      name: "express",
      // ⚠️ CRITICAL: Completely disable SSG – use a static sitemap.xml
      ssg: null,
    }),
  ],
}));

关键更改: ssg: null 会停止自动 sitemap 生成,这样你放在 public/ 中的文件将保持不变地被提供。


提供静态 Sitemap

public/sitemap.xml 中创建你希望被索引的 URL。

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://yourdomain.com/</loc>
    <lastmod>2024-12-01</lastmod>
    <changefreq>weekly</changefreq>
    <priority>1.0</priority>
  </url>
  <url>
    <loc>https://yourdomain.com/about/</loc>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
</urlset>

多语言示例

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://yourdomain.com/</loc>
    <priority>1.0</priority>
  </url>
  <url>
    <loc>https://yourdomain.com/es/</loc>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://yourdomain.com/de/</loc>
    <priority>0.8</priority>
  </url>
</urlset>

调整构建脚本

如果你的 package.json 中运行了 qwik city collect,请将其移除——否则 SSG 会再次被强制执行。

{
  "scripts": {
    // 之前
    // "build:deploy": "npm run build.production && qwik city collect"

    // 之后
    "build:deploy": "npm run build.production"
  }
}

运行构建并验证:

npm run build:deploy
ls -lh dist/sitemap.xml
cat dist/sitemap.xml

你应该看到完整的 sitemap 内容(而不是空文件)。

Node.js 服务器现在会将 dist/sitemap.xml 作为静态资源提供:

node server.js
curl http://localhost:3000/sitemap.xml

动态 Sitemap(SSR 路由)

如果需要在运行时生成 sitemap(例如从 CMS 获取),可以创建一个 SSR 路由。

src/routes/sitemap.xml/index.ts

import type { RequestHandler } from '@builder.io/qwik-city';

const SUPPORTED_LOCALES = ['en-US', 'es', 'ru', 'de', 'fr', 'ja'];

export const onGet: RequestHandler = async ({ url }) => {
  const origin = url.origin;

  const urls = [
    // Root URL
    `  
    ${origin}/
    ${new Date().toISOString().split('T')[0]}
    weekly
    1.0
  `,
    // Language‑specific URLs
    ...SUPPORTED_LOCALES
      .filter(l => l !== 'en-US')
      .map(l => `  
    ${origin}/${l}/
    weekly
    0.8
  `),
  ].join('\n');

  const sitemap = `

${urls}
`;

  return new Response(sitemap, {
    status: 200,
    headers: {
      'Content-Type': 'application/xml; charset=utf-8',
      'Cache-Control': 'public, max-age=86400', // 1 day
    },
  });
};

注意: 在适配器配置中仍然保持 ssg: null,以避免静态生成步骤覆盖此动态路由。


为什么 Node.js 适配器会运行 SSG

  • 静态站点生成(SSG) – 在构建时预渲染页面、生成 sitemap 并写入静态文件。
  • 服务器端渲染(SSR) – 在运行时按需渲染页面。

Node.js 适配器本意是用于 SSR,但默认仍会触发 SSG,以生成 sitemap 并预渲染任何带有 onStaticGenerate 的页面。当路由是动态的或缺少 onStaticGenerate 时,SSG 找不到可导出的路由,结果写入一个空的 sitemap。


常见陷阱

问题产生原因解决办法
ssg: { exclude: ['*'] }排除了文件但仍会运行 SSG,仍会覆盖 sitemap。使用 ssg: null
构建脚本中包含 qwik city collect在构建后显式运行 SSG,覆盖你的静态文件。删除该命令。
仅依赖 public/ 来放置动态内容动态路由需要 SSR 处理,而不是静态文件。按上文示例实现 SSR 路由。
每次构建都会清空 dist/静态文件应放在 public/,不要手动复制到 dist/保持 public/sitemap.xml

应用修复后的检查清单

  • 构建完成后不再出现 “Starting Qwik City SSG…”(当 ssg: null 时)。
  • dist/sitemap.xml 存在且大小大于空的 109 字节文件。
  • cat dist/sitemap.xml 显示预期的 URL 列表。
  • 生产服务器返回完整的 sitemap(curl https://yourdomain.com/sitemap.xml)。
  • 构建日志中不再出现与 SSG 相关的警告或错误。

可选:将 /sitemap.xml/ 重定向到 /sitemap.xml

如果请求了带斜杠的路径,可以在服务器入口中添加简单的重定向:

import { createServer } from "http";

createServer((req, res) => {
  if (req.url === "/sitemap.xml/") {
    res.writeHead(301, { Location: "/sitemap.xml" });
    res.end();
    return;
  }

  // ...rest of server logic
}).listen(3000);

该重定向对搜索引擎无害,但可以让 URL 更整洁。


静态 Sitemap 与 SSR 路由的选择

使用场景推荐方案
纯 SSR 应用,路由在开发时已知且 sitemap 很少变化静态 sitemappublic/sitemap.xml + ssg: null
sitemap 数据来自 CMS、数据库或用户生成内容SSR 路由src/routes/sitemap.xml/index.ts
需要实时更新或按语言区分的逻辑SSR 路由
部署简洁、无需额外构建步骤静态 sitemap

根据项目需求选择合适的方案,空 sitemap 的问题即可得到解决。

Back to Blog

相关文章

阅读更多 »

开源邮件预热:完整指南

引言 开源电子邮件预热是逐步与邮箱提供商建立信任的过程,使您的邮件进入收件箱,而不是垃圾邮件文件夹....