- 新增404页面,优化未找到页面体验,避免被搜索引擎索引 - 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理 - 实现/article、/case、/product及/page动态路由兼容列表与详情展示 - 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置 - 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持 - 模板增强支持CMS单页内容加载及SEO信息动态设置 - 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
15 KiB
SEO / GEO 优化诊断报告
项目:website-template(Nuxt 多租户 CMS 官网模板)
检查日期:2026-08-04
检查范围:Nuxt 配置、页面/模板 SEO、结构化数据、站点地图、robots、图片与性能、GEO 友好度
一、执行摘要
当前项目已具备 基础的 SEO 骨架:SSR 渲染、统一的 usePageSeo 工具、动态 robots.txt / sitemap.xml、Organization 结构化数据、旧链接 301 重定向等。
但 详情页 SEO 几乎空白,这是目前最大的流量损失点:/article/:id、/product/:id、/case/:id 的标题、描述、OG 信息都没有使用实际内容,导致搜索引擎看到的所有详情页都是统一的栏目名(如“新闻资讯 - 广西恒瑞天诚项目管理有限公司”)。
下方按 P0(立即修复)/ P1(强烈建议)/ P2(持续优化) 分级列出问题与落地建议。
二、已做 SEO 事项(可保留)
| 事项 | 位置 | 状态 |
|---|---|---|
| 全局 SSR,搜索引擎可抓取完整 HTML | nuxt.config.ts routeRules |
✅ |
| 统一页面 SEO 工具(TDK + OG + Twitter + Canonical) | app/composables/usePageSeo.ts |
✅ |
| Organization 结构化数据 | useOrganizationSeo |
✅ |
| 首页 SEO + 组织结构化数据 | app/pages/index.vue |
✅ |
| CMS 单页 SEO(title/keywords/description/photo) | app/pages/page/[id].vue |
✅ |
| 关于我们页 SEO | template-*/pages/About.vue |
✅ |
动态 robots.txt(开发环境屏蔽) |
server/api/robots.txt.ts |
✅ |
动态 sitemap.xml |
server/api/sitemap.xml.ts |
✅(但范围不足,见 P0) |
| 旧链接 301 重定向(/news→/article 等) | server/middleware/z-news-detail-redirect.ts |
✅ |
部分图片使用 loading="lazy" |
模板 Home | ✅ |
| 富文本图片响应式处理 | app/components/RichText.vue |
✅ |
三、问题清单与优化建议
🔴 P0 — 立即修复(影响搜索收录与排名)
1. 文章/产品/案例详情页没有使用内容标题做 SEO
问题:
useModuleRoute只给详情页设置了栏目兜底标题,如“新闻资讯 / 产品中心 / 案例展示”。- 所有模板中的
NewsDetail.vue、ProductDetail.vue、CaseDetail.vue均未调用usePageSeo。 - 截图中的
/article/396(招标代理)实际标题应该是“招标代理 - 广西恒瑞天诚项目管理有限公司”,但搜索引擎看到的是“新闻资讯 - 广西恒瑞天诚项目管理有限公司”。
影响:大量详情页在搜索结果中标题重复、CTR 低;长尾关键词无法被有效索引。
修复方案:在每个详情组件中,拿到数据后立即调用 usePageSeo,并注入 Article / Product 结构化数据。
示例(app/templates/template-08/pages/NewsDetail.vue):
const route = useRoute()
const id = route.params.id as string
const { siteInfo, fetchSiteInfo } = useSite()
await fetchSiteInfo()
const { data: article } = await useFetch<Article | null>(`/api/article/detail?id=${id}`, {
key: `article-${id}`
})
// 用文章内容覆盖 SEO
usePageSeo({
title: article.value?.title || '新闻资讯',
description: stripHtml(article.value?.summary || '').slice(0, 160) || undefined,
keywords: article.value?.tags?.join(',') || undefined,
path: route.path, // 注意用 route.path 而非 route.fullPath,避免 ?navId= 进入 canonical
image: article.value?.image || article.value?.cover || undefined,
type: 'article',
publishedTime: article.value?.publishTime || article.value?.createTime,
modifiedTime: article.value?.updateTime
}, siteInfo.value)
// 注入文章结构化数据
useJsonLd({
'@context': 'https://schema.org',
'@type': 'Article',
headline: article.value?.title,
description: stripHtml(article.value?.summary || ''),
image: article.value?.image || article.value?.cover || undefined,
datePublished: article.value?.publishTime || article.value?.createTime,
dateModified: article.value?.updateTime,
author: { '@type': 'Organization', name: siteInfo.value?.websiteName }
})
需同步修改的模板文件:
app/templates/template-0*/pages/NewsDetail.vue(9 套)app/templates/template-0*/pages/ProductDetail.vue(9 套)app/templates/template-0*/pages/CaseDetail.vue(9 套)
如果 9 套模板结构差异大,可先在
useModuleRoute中统一注入一份“最小可用 SEO”(标题用详情接口数据),再逐步让各模板补充更精细的 OG 图和结构化数据。
2. Sitemap 只包含单页,未覆盖文章/产品/案例详情和分页
问题:server/api/sitemap.xml.ts 只拉取 cms-website/pageAll(即 CMS 单页 slug),未包含:
- 文章详情页
/article/{id} - 产品详情页
/product/{id} - 案例详情页
/case/{id} - 栏目列表页
/article、/article/{navId}、/product、/case
影响:搜索引擎依赖 sitemap 发现新内容,缺失会导致收录速度慢、遗漏详情页。
修复方案:扩展 sitemap.xml.ts,增加 article/product/case 列表接口调用,并拆分 sitemapindex(如果 URL 数量 > 50,000 或体积 > 50MB)。
最小可用实现(伪代码):
// server/api/sitemap.xml.ts
const [pages, articles, products, cases] = await Promise.all([
fetchPages(),
fetchList('/cms/cms-article/page', { limit: 1000 }),
fetchList('/cms/cms-product/page', { limit: 1000 }),
fetchList('/cms/cms-case/page', { limit: 1000 })
])
const staticUrls = [
{ loc: '/', priority: '1.0', changefreq: 'daily' },
{ loc: '/article', priority: '0.9', changefreq: 'daily' },
{ loc: '/product', priority: '0.9', changefreq: 'daily' },
{ loc: '/case', priority: '0.9', changefreq: 'daily' }
]
const articleUrls = articles.map(a => ({
loc: `/article/${a.id || a.articleId}`,
priority: '0.7',
changefreq: 'weekly',
lastmod: a.updateTime || a.publishTime
}))
// ... product/case 同理
注意事项:
- 只输出
status === 0(已发布)的内容。 - 列表接口若支持
limit,需分页拉取全部;若总量大,建议生成sitemapindex.xml+ 多个子 sitemap。 - 列表页分页(
/article?page=2)可不必写入 sitemap,让搜索引擎通过页面内链发现即可。
3. Canonical URL 可能包含查询参数,导致重复内容
问题:多处使用 route.fullPath 作为 path 传给 usePageSeo:
useModuleRoute:path: route.fullPathapp/pages/page/[id].vue:path: route.fullPathapp/pages/[slug].vue:path: /${slug}(这个 OK)
当 URL 带 ?navId=xxx、?keywords=xxx、?page=2 时,canonical 会带上这些参数,造成同一内容多个 canonical。
修复方案:统一使用干净路径(route.path)作为 canonical,必要时把分页参数也排除。
// 建议增加一个干净 canonical 工具
function getCanonicalPath(route: RouteLocationNormalizedLoaded) {
// 只保留 path,去掉 navId/keywords/page 等查询参数
return route.path
}
对于列表分页,如果希望保留 ?page=2 的 canonical,可单独处理;否则建议所有列表页的 canonical 都不带查询参数。
4. 详情页没有面包屑结构化数据
问题:useBreadcrumbSeo 已定义但未被任何页面使用。
影响:Google 搜索结果可能无法展示面包屑导航,降低 SERP 丰富度。
修复方案:在详情页和列表页注入面包屑:
useBreadcrumbSeo([
{ name: '首页', url: '/' },
{ name: article.value?.categoryName || '新闻资讯', url: '/article' },
{ name: article.value?.title || '详情', url: route.path }
])
🟡 P1 — 强烈建议(提升 CTR 与收录质量)
5. 缺少 <html lang="zh-CN">
问题:nuxt.config.ts 的 app.head 未设置 htmlAttrs.lang。
影响:搜索引擎无法准确判断页面语言;屏幕阅读器体验受损。
修复方案:
app: {
head: {
htmlAttrs: { lang: 'zh-CN' },
// ...
}
}
6. 404 页面没有 noindex
问题:app/pages/404.vue 没有设置 robots: noindex。
修复方案:
useSeoMeta({
robots: 'noindex, follow',
title: '页面未找到'
})
7. 标题模板未统一站点名后缀
问题:nuxt.config.ts 中 titleTemplate: '%s' 只是原样输出,各页面需自己拼 - 站点名。目前 usePageSeo 会自动拼接,但兜底标题(如 app/pages/[slug].vue 中的 slug)可能出现没有站点名的情况。
建议:把全局 titleTemplate 改为 ' %s - {{siteName}}',或在 usePageSeo 中统一处理。当前 usePageSeo 已经拼接,可作为单一事实源。
8. 图片懒加载与 CLS 优化不足
问题:
- 多数模板图片没有
loading="lazy"。 - 所有图片缺少
width/height或aspect-ratio,导致 Cumulative Layout Shift(CLS)。 - 没有
decoding="async"、没有响应式srcset/sizes。 - 装饰性/无意义图片没有
alt=""。
修复方案:
<img
:src="fileUrl(item.cover)"
:alt="item.productName || ''"
loading="lazy"
decoding="async"
width="800"
height="450"
class="w-full h-full object-cover"
>
对于 CMS 富文本中的图片,可在 RichText.vue 中统一注入 loading="lazy" decoding="async"。
9. Open Graph 图片缺失或 fallback 到站点 Logo
问题:usePageSeo 在没有 input.image 时会 fallback 到 site.websiteLogo。站点 Logo 通常是 PNG/SVG,尺寸可能不符合 OG 推荐(1200×630)。
建议:
- 列表页/首页配置一张专门的 OG 封面图(可在 CMS
siteInfo中增加ogImage字段)。 - 详情页优先使用内容封面图,并确保图片可被公开访问、尺寸合适。
10. 分页缺少 rel="prev/next" 或规范处理
问题:文章/产品列表页使用按钮分页,没有 <a> 标签链接,也没有 rel="prev/next"。
影响:搜索引擎可能无法顺畅抓取深层列表页。
建议:
- 分页按钮使用
<NuxtLink>而不是<button @click>,使列表页 URL 变为可抓取的链接。 - 在列表页
head中注入:
if (currentPage.value > 1) {
useHead({
link: [
{ rel: 'prev', href: `/article${currentPage.value === 2 ? '' : '?page=' + (currentPage.value - 1)}` }
]
})
}
if (currentPage.value < totalPages.value) {
useHead({
link: [
{ rel: 'next', href: `/article?page=${currentPage.value + 1}` }
]
})
}
🟢 P2 — 持续优化(GEO 与长期竞争力)
11. GEO:内容结构不利于 AI 引擎引用
问题:
- 没有 FAQ / HowTo 结构化数据。
- 富文本内容层级不清晰,AI 摘要时难以提取要点。
- 服务/产品页面多为纯列表,缺少“问题-答案”式内容块。
建议:
- 在“关于我们”、“服务范围”等页面增加 FAQ 区块,并注入
FAQPage结构化数据:
useJsonLd({
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: [
{
'@type': 'Question',
name: '招标代理服务包含哪些内容?',
acceptedAnswer: {
'@type': 'Answer',
text: '包括设计招标、监理招标、施工招标、材料及设备招标、政府采购招标等全流程代理服务。'
}
}
]
})
- 产品/服务详情页使用清晰的 H2/H3 小标题,每个小节回答一个具体问题。
- 在内容中自然覆盖“是什么 / 怎么做 / 多少钱 / 有什么优势”等 AI 常见引用角度。
12. 内部链接优化
问题:
- “返回新闻列表”链接使用
?navId=,canonical 处理不好时会造成参数化 URL 泛滥。 - 列表页卡片链接使用
/article/${id},但没有在正文中增加相关内容推荐链接。
建议:
- 详情页底部增加“相关文章/产品/案例”模块,提升内链密度与页面权重流动。
- 面包屑使用干净路径链接。
13. 站点速度 / Core Web Vitals
问题:
- 没有图片 CDN 压缩或 WebP/AVIF 格式适配。
- 没有预连接 DNS / 预加载关键资源。
- 第三方字体(如有)可能阻塞渲染。
建议:
- 在
nuxt.config.ts增加:
app: {
head: {
link: [
{ rel: 'preconnect', href: 'https://server.websoft.top' },
{ rel: 'dns-prefetch', href: 'https://server.websoft.top' }
]
}
}
- 对 CMS 图片 URL 增加压缩参数(如 OSS 图片处理
?x-oss-process=image/resize,w_1200)。 - 评估引入
@nuxt/image模块,自动实现懒加载、响应式、格式降级。
14. 搜索功能与搜索关键词页面
问题:搜索结果页(?keywords=xxx)可能被搜索引擎索引,造成低质量页面。
建议:在搜索结果页或无结果页注入 noindex:
if (route.query.keywords) {
useSeoMeta({ robots: 'noindex, follow' })
}
四、推荐落地顺序
| 顺序 | 任务 | 预期收益 |
|---|---|---|
| 1 | 为所有详情页注入 usePageSeo + Article/Product 结构化数据 |
解决标题重复问题,提升长尾词收录 |
| 2 | 扩展 sitemap.xml 覆盖 article/product/case |
加速收录,减少遗漏 |
| 3 | Canonical 统一使用 route.path,排除查询参数 |
避免重复内容,集中权重 |
| 4 | 注入面包屑结构化数据 | 提升 SERP 展示丰富度 |
| 5 | 设置 html lang="zh-CN"、404 noindex、OG 图片优化 |
完善基础 SEO 细节 |
| 6 | 图片懒加载、width/height、CLS 优化 | 提升 Core Web Vitals |
| 7 | FAQ / HowTo 结构化数据 + 内容结构化 | 提升 GEO/AI 引用概率 |
五、关键文件清单
| 文件 | 当前作用 | 建议改动 |
|---|---|---|
app/composables/usePageSeo.ts |
统一 SEO 工具 | 保持现状,可被详情页复用 |
app/composables/useModuleRoute.ts |
模块路由 + 基础 SEO | 列表页 SEO 已 OK;详情页改为用 route.path |
app/templates/template-0*/pages/NewsDetail.vue |
文章详情 | 增加 usePageSeo + useJsonLd |
app/templates/template-0*/pages/ProductDetail.vue |
产品详情 | 增加 usePageSeo + useJsonLd |
app/templates/template-0*/pages/CaseDetail.vue |
案例详情 | 增加 usePageSeo + useJsonLd |
server/api/sitemap.xml.ts |
动态站点地图 | 扩展 article/product/case 列表 |
server/api/robots.txt.ts |
robots.txt | 保持现状 |
nuxt.config.ts |
全局配置 | 增加 htmlAttrs.lang、preconnect |
app/pages/404.vue |
404 页面 | 增加 noindex |
app/components/RichText.vue |
富文本渲染 | 统一为图片注入 lazy/decoding |
六、结论
项目已经有不错的 SEO 基础架构,但 详情页 SEO 是当前最大的短板。修复后,预计:
- 详情页长尾关键词收录量显著提升;
- 搜索结果标题/描述点击率提升;
- AI 搜索引擎(GEO)引用站点内容的概率提高。
建议优先完成 P0 四项(详情页 SEO、Sitemap、Canonical、面包屑),再逐步推进图片性能与 GEO 结构化数据。