Next.js项目中如果同时运行多个域名,且站点包含静态路由和动态路由两种页面类型,生成Sitemap时需要分别处理不同域名的路径映射,同时覆盖所有可访问的页面,才能满足搜索引擎的收录要求。

多域名Sitemap的核心需求
多域名场景下的Sitemap生成需要满足几个核心要求:首先每个域名对应独立的Sitemap文件,避免不同域名的路径混淆;其次静态路由和动态路由的页面都要被完整收录;最后Sitemap的内容要符合搜索引擎的XML规范,包含页面的更新时间、优先级等必要字段。
静态路由的采集逻辑
静态路由是项目中手动定义的页面路径,比如pages/about.tsx对应的/about路径,这类路径可以直接从项目的文件结构中提取。我们可以通过读取pages目录下的文件,过滤掉_app.tsx、_document.tsx等系统文件,再拼接成完整的路径。
以下是提取静态路由的示例代码:
const fs = require('fs');
const path = require('path');
// 获取pages目录下的所有静态路由
function getStaticRoutes() {
const pagesDir = path.join(process.cwd(), 'pages');
const staticRoutes = [];
// 递归读取pages目录下的文件
function readDir(dir, basePath = '') {
const files = fs.readdirSync(dir);
files.forEach(file => {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);
// 如果是目录,递归处理
if (stat.isDirectory()) {
readDir(filePath, path.join(basePath, file));
} else {
// 过滤系统文件和API路由
if (file.startsWith('_') || file.startsWith('api') || file.endsWith('.tsx') === false) return;
// 处理文件路径为路由路径
let routePath = path.join(basePath, file.replace(/.tsx$/, ''));
// 处理index文件
if (routePath.endsWith('index')) {
routePath = routePath.replace(/index$/, '');
}
// 处理动态路由参数,这里静态路由暂时忽略动态参数文件
if (routePath.includes('[')) return;
staticRoutes.push(routePath || '/');
}
});
}
readDir(pagesDir);
return staticRoutes;
}
动态路由的采集逻辑
动态路由的页面路径依赖后端数据,比如pages/post/[id].tsx对应的文章详情页,需要先从数据库或者接口中获取所有文章的ID,再拼接成对应的路径。我们可以定义一个动态路由的映射配置,指定每个动态路由对应的数据获取方法。
以下是动态路由配置的示例代码:
// 动态路由配置,key是路由模板,value是获取参数的方法
const dynamicRouteConfig = {
'/post/[id]': async () => {
// 这里模拟从数据库获取所有文章ID,实际项目中替换为真实的数据请求
const posts = [{ id: '1' }, { id: '2' }, { id: '3' }];
return posts.map(post => `/post/${post.id}`);
},
'/category/[slug]': async () => {
// 模拟获取所有分类slug
const categories = [{ slug: 'tech' }, { slug: 'life' }];
return categories.map(cat => `/category/${cat.slug}`);
}
};
// 获取所有动态路由
async function getDynamicRoutes() {
const dynamicRoutes = [];
for (const [template, getData] of Object.entries(dynamicRouteConfig)) {
const routes = await getData();
dynamicRoutes.push(...routes);
}
return dynamicRoutes;
}
多域名Sitemap生成实现
接下来需要整合静态和动态路由,再根据不同的域名生成对应的Sitemap。我们可以定义一个域名和路由前缀的映射关系,比如domain1.com对应/site1前缀,domain2.com对应/site2前缀,生成Sitemap时自动拼接对应的域名和路径。
完整的Sitemap生成代码如下:
const { getStaticRoutes } = require('./staticRoutes');
const { getDynamicRoutes } = require('./dynamicRoutes');
// 域名配置,key是域名,value是该域名下的路径前缀
const domainConfig = {
'https://ipipp.com': '',
'https://blog.ipipp.com': '/blog'
};
// 生成单个域名的Sitemap XML
async function generateSitemapForDomain(domain, pathPrefix) {
// 获取所有路由
const staticRoutes = getStaticRoutes();
const dynamicRoutes = await getDynamicRoutes();
const allRoutes = [...staticRoutes, ...dynamicRoutes];
// 过滤出属于当前域名的路由
const domainRoutes = allRoutes.filter(route => route.startsWith(pathPrefix));
// 拼接Sitemap XML内容
let xml = '<?xml version="1.0" encoding="UTF-8"?>';
xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
domainRoutes.forEach(route => {
// 去掉路径前缀,拼接完整域名路径
const fullPath = route.replace(pathPrefix, '') || '/';
const fullUrl = `${domain}${fullPath}`;
xml += '<url>';
xml += `<loc>${fullUrl}</loc>`;
xml += `<lastmod>${new Date().toISOString()}</lastmod>`;
xml += '<changefreq>daily</changefreq>';
xml += '<priority>0.7</priority>';
xml += '</url>';
});
xml += '</urlset>';
return xml;
}
// 生成所有域名的Sitemap
async function generateAllSitemaps() {
const sitemaps = {};
for (const [domain, prefix] of Object.entries(domainConfig)) {
const sitemapXml = await generateSitemapForDomain(domain, prefix);
sitemaps[domain] = sitemapXml;
}
return sitemaps;
}
// 导出生成方法,可在Next.js的API路由中调用
module.exports = { generateAllSitemaps };
在Next.js中集成Sitemap生成
我们可以将Sitemap生成逻辑放在Next.js的API路由中,当用户访问/sitemap.xml时,根据请求的域名返回对应的Sitemap内容。以下是API路由的实现代码:
// pages/api/sitemap.xml.js
import { generateAllSitemaps } from '../../lib/sitemapGenerator';
export default async function handler(req, res) {
const domain = `https://${req.headers.host}`;
const sitemaps = await generateAllSitemaps();
const sitemapXml = sitemaps[domain];
if (!sitemapXml) {
res.status(404).end('Sitemap not found');
return;
}
res.setHeader('Content-Type', 'application/xml');
res.write(sitemapXml);
res.end();
}
注意事项
- 动态路由的数据获取方法需要根据实际业务调整,确保能获取到所有可访问的页面参数,避免遗漏页面。
- 如果站点内容更新频繁,可以设置定时任务重新生成Sitemap,或者在内容更新时触发Sitemap更新。
- 生成的Sitemap需要提交到各搜索引擎的站长平台,才能更快被收录。
- 如果某个域名下没有对应的路径,需要返回404状态,避免返回空内容的Sitemap。