Files
hjc-web/outputs/seo-geo-audit-2026-08-04.md
T
gxwebsoft 2b69686795 feat(app): 添加多模板关于我们页面及相关路由和404页面
- 新增404页面,优化未找到页面体验,避免被搜索引擎索引
- 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理
- 实现/article、/case、/product及/page动态路由兼容列表与详情展示
- 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置
- 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持
- 模板增强支持CMS单页内容加载及SEO信息动态设置
- 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
2026-09-08 12:13:44 +08:00

419 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SEO / GEO 优化诊断报告
> 项目:website-templateNuxt 多租户 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 单页 SEOtitle/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`**
```ts
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)。
**最小可用实现(伪代码)**
```ts
// 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.fullPath`
- `app/pages/page/[id].vue``path: route.fullPath`
- `app/pages/[slug].vue``path: /${slug}`(这个 OK
当 URL 带 `?navId=xxx``?keywords=xxx``?page=2` 时,canonical 会带上这些参数,造成同一内容多个 canonical。
**修复方案**:统一使用**干净路径**`route.path`)作为 canonical,必要时把分页参数也排除。
```ts
// 建议增加一个干净 canonical 工具
function getCanonicalPath(route: RouteLocationNormalizedLoaded) {
// 只保留 path,去掉 navId/keywords/page 等查询参数
return route.path
}
```
对于列表分页,如果希望保留 `?page=2` 的 canonical,可单独处理;否则建议所有列表页的 canonical 都不带查询参数。
---
#### 4. 详情页没有面包屑结构化数据
**问题**`useBreadcrumbSeo` 已定义但未被任何页面使用。
**影响**:Google 搜索结果可能无法展示面包屑导航,降低 SERP 丰富度。
**修复方案**:在详情页和列表页注入面包屑:
```ts
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`
**影响**:搜索引擎无法准确判断页面语言;屏幕阅读器体验受损。
**修复方案**
```ts
app: {
head: {
htmlAttrs: { lang: 'zh-CN' },
// ...
}
}
```
---
#### 6. 404 页面没有 `noindex`
**问题**`app/pages/404.vue` 没有设置 `robots: noindex`
**修复方案**
```ts
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 ShiftCLS)。
- 没有 `decoding="async"`、没有响应式 `srcset/sizes`
- 装饰性/无意义图片没有 `alt=""`
**修复方案**
```html
<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` 中注入:
```ts
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` 结构化数据:
```ts
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` 增加:
```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`
```ts
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 结构化数据。