Next.js App Router中如何静态生成站点地图(Sitemap)?
Next.js App Router中如何静态生成站点地图(Sitemap)?
首先,先解答你关于sitemap.ts调用频率和函数调用消耗的核心疑问:
在Next.js App Router中,app/sitemap.ts本质是一个特殊的Route Handler,每次有请求访问/sitemap.xml时(比如搜索引擎爬虫抓取),它都会完整执行一次——包括调用getBlogPosts拉取博客数据。这确实会消耗Vercel免费计划的函数调用额度,尤其是爬虫频繁访问的话,消耗会比你预期的更快。
接下来针对你想静态生成sitemap的需求,分两种场景给出可落地的方案:
方案1:手动创建静态sitemap.xml(适合内容不常更新的场景)
你提到的直接在public文件夹放置sitemap.xml的思路完全可行,Next.js会自动暴露public文件夹下的所有静态资源,包括这个站点地图文件。
具体操作步骤:
- 创建public文件夹:如果你的项目里没有
public目录,直接在项目根目录手动新建一个就行(和app文件夹同级)——Next.js会自动识别这个目录,不需要额外配置。 - 编写静态sitemap.xml:在
public目录下新建sitemap.xml文件,按照sitemap官方规范编写内容,示例如下:
<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <!-- 首页 --> <url> <loc>https://portfolio-blog-starter.vercel.app/</loc> <lastmod>2024-05-20</lastmod> </url> <!-- 博客列表页 --> <url> <loc>https://portfolio-blog-starter.vercel.app/blog</loc> <lastmod>2024-05-20</lastmod> </url> <!-- 单个博客文章(按需添加) --> <url> <loc>https://portfolio-blog-starter.vercel.app/blog/your-first-post</loc> <lastmod>2024-05-15</lastmod> </url> </urlset>
- 验证生效:部署后访问
https://your-domain.com/sitemap.xml,能看到你编写的静态内容就说明成功了。此时app/sitemap.ts会被静态文件覆盖,不会再执行任何函数调用。
方案2:构建时自动生成静态sitemap.xml(适合内容有更新,但可预先生成的场景)
如果你的博客内容会更新,但可以在部署阶段预先生成所有博客链接,那么可以写一个脚本,在Next.js构建时自动生成sitemap.xml到public文件夹——既不用手动维护链接,也不会在运行时消耗函数调用额度。
具体操作步骤:
- 创建生成脚本:在项目根目录新建
scripts/generate-sitemap.js文件,内容如下:
import { getBlogPosts } from "../app/server/actions"; import fs from "fs"; import path from "path"; const baseUrl = "https://portfolio-blog-starter.vercel.app"; async function generateSitemap() { // 拉取所有博客文章数据 const { posts } = await getBlogPosts(); // 拼接sitemap的XML内容 const urlSet = `<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <!-- 静态路由 --> <url> <loc>${baseUrl}</loc> <lastmod>${new Date().toISOString().split("T")[0]}</lastmod> </url> <url> <loc>${baseUrl}/blog</loc> <lastmod>${new Date().toISOString().split("T")[0]}</lastmod> </url> <!-- 动态博客路由 --> ${posts.map(post => ` <url> <loc>${baseUrl}/blog/${post.slug}</loc> <lastmod>${post.metadata.publishedAt}</lastmod> </url> `).join("")} </urlset>`; // 确保public文件夹存在 const publicDir = path.join(process.cwd(), "public"); if (!fs.existsSync(publicDir)) { fs.mkdirSync(publicDir); } // 将内容写入sitemap.xml文件 fs.writeFileSync(path.join(publicDir, "sitemap.xml"), urlSet); } // 执行生成逻辑,失败则抛出错误终止构建 generateSitemap().catch(err => { console.error("生成sitemap失败:", err); process.exit(1); });
- 修改构建命令:在
package.json中,让build命令先执行生成脚本:
{ "scripts": { "prebuild": "node scripts/generate-sitemap.js", "build": "next build", // 保留其他原有脚本 } }
- 清理动态sitemap文件:删除
app/sitemap.ts,避免静态文件和动态路由冲突(静态文件优先级更高,但删除更保险)。 - 部署验证:运行
npm run build时,脚本会自动拉取博客数据并生成sitemap.xml到public文件夹,部署后访问/sitemap.xml就能看到包含所有博客链接的静态站点地图,运行时不会再触发任何函数调用。
关键注意事项:
- 静态sitemap的唯一缺点是:如果部署后新增/修改博客内容,sitemap不会自动同步,需要重新运行构建脚本并部署才能更新。如果需要动态更新,可能要结合ISR或定时构建,但这已经超出纯静态生成的范畴了。
- 为什么项目初始没有public文件夹?因为Next.js App Router项目默认不会自动创建
public目录,你完全可以手动创建,Next.js会自动处理该目录下的所有静态资源。
内容来源于stack exchange
相关产品推荐
相关产品推荐

