今日分享:Nuxt 项目生成sitemap.xml

JiHua
2025-09-19
35

在 SEO 优化中,sitemap.xml 是搜索引擎快速抓取网站结构的关键文件。对于前后端分离的项目,如果前端是 Nuxt,后端是 Node.js,我们如何优雅地生成 sitemap.xml 呢?本文将带你完整梳理。


1 为什么 Nuxt 前端不适合生成 sitemap.xml

很多开发者会尝试在 Nuxt 前端生成 sitemap.xml,但会遇到几个问题:

  1. Nuxt SPA 路由会拦截请求

    • 当你访问 /sitemap.xml 时,Nuxt 会尝试匹配前端路由,没有对应页面就报 404。
  2. 搜索引擎抓取 SPA 时无法执行 JS

    • 前端生成的 sitemap.xml 只有在浏览器运行时才存在,而搜索引擎不会执行前端 JS。
  3. 前端无法直接访问数据库

    • sitemap.xml 需要动态获取文章、通知等 URL,前端无法安全地直接访问数据库。

√ 结论:sitemap.xml 应该由后端生成


2 Node.js 后端生成 sitemap.xml

假设你的后端是 Express + MySQL(或其他数据库),可以按以下步骤生成:

A. 准备数据

假设你有两类数据:文章和通知:

const article = await ExecuteFunc(
  'SELECT article_id AS id, pub_date FROM ev_articles WHERE state = 0 AND is_delete = 0'
)
const notify = await ExecuteFunc(
  'SELECT notify_id AS id, pub_date FROM ev_notify WHERE whosee = 0 AND state = 0 AND is_delete = 0'
)

我们可以给每条记录加一个 type 字段,区分 URL 前缀:

const articlesWithType = article.map(a => ({ ...a, type: 'article' }))
const notifyWithType = notify.map(n => ({ ...n, type: 'notify' }))
const data = [...articlesWithType, ...notifyWithType]

B. 拼接 XML 字符串

const hostname = 'https://a.com'

const xml = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${data.map(a => `
  <url>
    <loc>${hostname}/${a.type}/${a.id}</loc>
    <lastmod>${a.pub_date}</lastmod>
    <changefreq>daily</changefreq>
    <priority>0.8</priority>
  </url>`).join('')}
</urlset>`

说明:

  • <loc> 指定页面 URL
  • <lastmod> 页面最后更新时间
  • <changefreq> 页面更新频率
  • <priority> 页面重要性(0.0 ~ 1.0,搜索引擎参考值)

C. 返回 XML 给前端或搜索引擎

app.get('/sitemap.xml', (req, res) => {
  res.header('Content-Type', 'application/xml')
  res.send(xml)
})

这样,当搜索引擎访问 https://a.com/sitemap.xml 时,就能直接拿到动态生成的 XML。


3 解决 Nuxt SPA 拦截问题

由于前端 Nuxt SPA 会拦截请求,我们可以通过代理或服务器路由避免:

Nginx 代理示例:

server {
    listen 80;
    server_name a.com;

    location / {
        proxy_pass http://127.0.0.1:777/;  # Nuxt 前端
    }

    location /api/ {
        proxy_pass http://127.0.0.1:666/api/;  # Node.js API
    }

    location /sitemap.xml {
        proxy_pass http://127.0.0.1:666/sitemap.xml; # sitemap.xml 由后端提供
    }
}

√ 这样:

  • 搜索引擎直接访问 /sitemap.xml → 拿到 XML
  • Nuxt 前端不会拦截
  • 前端完全不接触数据库

4 扁平化静态页面

除了动态文章和通知,你可能还有静态页面,例如:

const staticPages = [
  { path: '/', pub_date: '2025-09-19' },
  { path: '/Login', pub_date: '2025-09-19' },
  { path: '/register', pub_date: '2025-09-19' }
]

同样可以合并到 sitemap:

const allPages = [...staticPages, ...data.map(d => ({
  path: `/${d.type}/${d.id}`,
  pub_date: d.pub_date
}))]

然后生成 XML 即可。


5 总结

  1. sitemap.xml 必须由后端生成,前端 SPA 不适合生成
  2. 动态文章 / 通知 + 静态页面 可以统一生成
  3. 设置 Content-Type 为 application/xml
  4. 通过代理或服务器路由解决 Nuxt SPA 拦截问题

这样不仅搜索引擎可以抓取,还能保证前端安全和项目规范。


完整express.js代码


// 做站点地图
exports.sitemapData = async (req, res) => {
  const article = await ExecuteFunc(
    'SELECT article_id AS id, pub_date FROM ev_articles WHERE state = 0 AND is_delete = 0')
  const notify = await ExecuteFunc(
    'SELECT notify_id AS id, pub_date FROM ev_notify WHERE whosee = 0 AND state = 0 AND is_delete = 0'
  )
  const rawData = [
    {
      pages: [
        { path: '/' },
        { path: '/Login' },
        { path: '/register' },
        { path: '/checkVer' },
        { path: '/DevProcess' },
        { path: '/SpsList' },
      ]
    },
    {
      pages: [
        { path: '/Notify' },
        { path: '/Search' },
      ]
    },
    {
      pages: [
        { path: '/space/jihua' },
        { path: '/feedback/class/spslist' },
        { path: '/feedback/sitemap' },
      ]
    },
    {
      pages: [
        { path: '/error/type-window' },
        { path: '/error/type-phone' },
        { path: '/error/test' },
      ]
    },
  ]

  // 获取今天日期,格式 YYYY-MM-DD
  const today = new Date().toISOString().split('T')[0]

  // 扁平化
  const sitemapArray = rawData.flatMap(category =>
    category.pages.map(page => ({
      path: page.path,
      pub_date: today
    }))
  )
  const hostname = 'http://j-h.top'
  // 拼接 XML 字符串
  const xml = `<?xml version="1.0" encoding="UTF-8"?>
              <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
              // 这是基本page,需要单独提取
              ${sitemapArray
      .map(
        (p) => `
                <url>
                  <loc>${hostname}${p.path}</loc>
                  <lastmod>${p.pub_date}</lastmod>
                  <changefreq>daily</changefreq>
                  <priority>1</priority>
                </url>`
      )
      .join('')}
      // 假如说你的路由有/article/**那就...
                ${article
      .map(
        (a) => `
                  <url>
                    <loc>${hostname}/article/${a.id}</loc>
                    <lastmod>${a.pub_date}</lastmod>
                    <changefreq>daily</changefreq>
                    <priority>0.8</priority>
                  </url>`
      )
      .join('')}
      // 假如说你的路由有/notify/**那就...
                ${notify
      .map(
        (b) => `
                  <url>
                    <loc>${hostname}/notify/${b.id}</loc>
                    <lastmod>${b.pub_date}</lastmod>
                    <changefreq>daily</changefreq>
                    <priority>0.8</priority>
                  </url>`
      )
      .join('')}
                </urlset>`

  // 设置返回类型为 XML
  res.header('Content-Type', 'application/xml')
  res.send(xml)
}


-end 本文由chatgpt梳理汇编文章
编辑:JiHua

标签:Nuxt、sitemap、内容分享、小技巧