2b69686795
- 新增404页面,优化未找到页面体验,避免被搜索引擎索引 - 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理 - 实现/article、/case、/product及/page动态路由兼容列表与详情展示 - 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置 - 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持 - 模板增强支持CMS单页内容加载及SEO信息动态设置 - 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
419 lines
15 KiB
Markdown
419 lines
15 KiB
Markdown
# 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`)**:
|
||
|
||
```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 Shift(CLS)。
|
||
- 没有 `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 结构化数据。
|