SEO 最佳实践
Swift Web 业务以内容驱动,搜索引擎流量是广告收入的命脉,SEO 不是附加项,而是产品的一等公民。本指南面向 Swift Web 全体开发人员,从 useSeo 长度告警一直讲到 Core Web Vitals 与 GEO,配套 framework 内部最佳实践。
引言
为什么我们要把 SEO 做透:
- 公司业务靠网站内容吸引用户、靠搜索引擎自然流量赚广告费
- 一个页面 SEO 做好 vs 做差,自然流量可以差 5–10 倍
- 现代搜索引擎(Google / Bing)+ AI 答案引擎(ChatGPT Search、Perplexity、Bing Copilot)对内容质量、结构化、性能、可访问性的要求只增不减
本指南覆盖 12 个章节 + 2 个附录,第一章与 useSeo dev console 告警直接挂钩(开发者从 console 告警点链接第一眼看到),后面章节是面向开发者的 SEO 知识小课堂,让你"原来 SEO 学问这么多"。
权威源:Google Search Central / Bing Webmaster Guidelines / Schema.org / web.dev (Core Web Vitals) / W3C WCAG。
一、TDK 字段:title / description / keywords
这是 useSeo 在 dev 环境的 console 告警直接相关章节。
1.1 长度建议来自哪里
| 字段 | 软建议 | 硬上限 | 来源 |
|---|---|---|---|
| title | 10–60 显示宽度 | > 70 触发关键词堆砌 | Google SERP ~60、Bing ~65 截断 |
| description | 50–160 显示宽度 | > 200 触发反作用 | Google SERP ~155、社交平台 ~200 |
| keywords | ≤10 个、≤200 字符 | 现代 SEO 不重视 | 反向触发关键词堆砌检测 |
title / description 在 SERP(搜索结果页)有视觉截断阈值,超过会被搜索引擎自动截断;过短则点击率(CTR)受损。keywords meta 现代搜索引擎已不参与排名权重,但堆砌仍会触发反作弊。
::: tip 单位是"显示宽度",不是字符数
表里的数字单位是 显示宽度(visual width),不是 JS .length:
- ASCII 字符(英文 / 数字 / 半角符号)算 1
- CJK 字符(中文 / 繁体 / 全角符号 / CJK 标点)算 2
阈值数字按英文 SERP 业界标准设计,中文文案目标长度要减半:
| 语言 | title 软建议 | description 软建议 |
|---|---|---|
| 英文 | 10–60 字符 | 50–160 字符 |
| 中文 / 繁体 | 5–30 字 | 25–80 字 |
为什么这么算:CJK 字符在 SERP 摘要里视觉宽度约为英文 2 倍,按 .length 一刀切会让
中文文案"实际超长"仍不报警、英文短文案"实际正常"反被判偏短。useSeo
内部用 displayWidth 算法(已导出供单测使用),告警文案里也直接显示 (显示宽度 N) 提醒开发者。
:::
1.2 短不一定不好(silenceLengthWarnings 用法)
工具型 / 私有页(登录、注册、个人中心、中转页、demo)天然简短:
- title
登录比用户登录入口 - Swift Web更直接 - description 同理,强行堆 50 字会稀释信息密度
这类页通常已标 robots: 'noindex' | 'private',不参与搜索引擎收录,长度告警在它们身上无意义。
对策:传 silenceLengthWarnings: true 一次性静音长度类告警。
useSeo({
title: '登录',
description: '账号登录',
robots: 'private',
silenceLengthWarnings: true,
})
1.3 但"过长"不一样 —— 那是反作用告警
带 ⚠️ 的告警(title > 70、description > 200、keywords > 10 个)是真有反作用的:现代搜索引擎判定为关键词堆砌(keyword stuffing),降低排名甚至触发惩罚。
silenceLengthWarnings: true 虽然会一并屏蔽这类提示文字,但行为本身是开发者要避免的 —— 静音不代表"没事了"。
1.4 公开页该补就补,别偷懒静音
indexable 公开页(首页、详情页、列表页)description 偏短是真实的 SEO 优化机会,应该补长(说清楚页面卖点 + 关键搜索词)。不要用 silenceLengthWarnings 应付。
// 改前(24 字,触发偏短)
useSeo({
title: '应用中心 - 禁漫天堂',
description: '禁漫天堂应用中心,精选博彩、娱乐、棋牌等优质应用',
})
// 改后(~80 字,覆盖搜索意图)
useSeo({
title: '应用中心 - 禁漫天堂',
description: '禁漫天堂应用中心精选博彩、娱乐、棋牌等优质 app,安全可靠免下载,支持秒级登录与跨设备同步,更多手游推荐持续更新中',
})
详情页要用后端 SEO 节点兜底链:
useSeo({
title: data?.seo?.title ?? data?.name,
description: data?.seo?.description ?? data?.summary,
image: data?.seo?.image ?? data?.cover,
type: 'article',
})
二、内容质量:E-E-A-T 与 GEO(AI 搜索时代)
2.1 E-E-A-T 框架
Google 2026 搜索质量评估指南强调 E-E-A-T:
- Experience — 第一手经验、亲身使用过、真实测评
- Expertise — 专业知识、学术背景、行业经验
- Authoritativeness — 权威性,被同行/媒体引用
- Trustworthiness — 可信度,事实可验证、来源可追溯
E-E-A-T 不是直接排名因子,但 Google 算法更新("helpful content update"、"core update")会把它放入打分。业务表现:广告产品页讲清楚"为什么我们的应用安全/合法/有用户口碑";编辑性内容(评测、攻略)署名作者并展示作者资质。
2.2 AI 生成内容:能用,但要加人类价值
Google 2026 立场:"AI 生成内容是 OK 的,前提是它为用户提供真实价值"。
- ✅ 用 AI 起草 + 人工修订、加事实核查、加亲身体验、加专业判断
- ❌ 大量低质 AI 内容刷 SEO(scaled content abuse)—— 算法直接降权或剔除
- ❌ 收购过期域名挂垃圾内容(expired domain abuse)
- ❌ 借用大站权威发垃圾子域 / 子目录(site reputation abuse)
2.3 GEO:Generative Engine Optimization
Bing 2026 webmaster 指南首次正式定义 GEO:让内容能被 AI 答案引擎(Copilot / ChatGPT Search / Perplexity)"grounding"(引用作为来源)。
实操:
- NOARCHIVE meta 阻止内容被 AI 引用(如内部草稿、付费内容)
- 结构化数据(见第八章)让 AI 更容易抽取事实
- 清晰的标题层级 + 段落 topic sentence,方便 AI 摘要
- 可验证的事实声明(带链接、带日期、带数据来源)
<!-- 拒绝 AI 引用:仅服务于人类访问者 -->
<meta name="robots" content="noarchive">
三、渲染策略与 Crawlability
3.1 渲染模式决策树
Nuxt 4 通过 defineRouteRules() 显式声明每个页面的渲染策略 —— Swift Web 强制要求每个页面都声明(否则 ESLint 报错)。
| 模式 | 适用场景 | SEO 影响 |
|---|---|---|
| isr (ISR) | 列表页、详情页、首页 | ✅ 缓存友好,SEO 一等公民 |
| ssr | 用户态强相关页(购物车、结算) | ✅ 完整 SEO,但服务器压力大 |
csr (ssr: false) | 个人中心、后台、登录态页 | ❌ 失去 SEO,仅在不需要被索引时用 |
| prerender (SSG) | 真静态页(关于、协议)—— 但默认建议改用 isr: 3600,见下 | ✅ 最快;不能用于动态参数页 [id].vue |
注意:prerender: true 用在 [id].vue 会全量爆炸(每个 id 生成一个静态文件)。
3.2 失去 SEO 的代价
ssr: false 等于把页面交给客户端 JS 渲染,搜索引擎爬虫看到的是空 HTML 骨架,没有 title / description / 主体内容。Google 虽然支持 JS 渲染("two-wave indexing"),但延迟 + 不稳定,禁止用于公开内容页。
只有以下场景可以用 csr:
- 强登录态页(个人中心、订单、钱包)
- 临时活动入口(不需要被索引的运营页)
3.3 robots:noindex / private 简写
Swift Web useSeo 暴露三种 robots 简写:
useSeo({ robots: 'noindex' }) // 等价 'noindex, follow',中转页 / 站内搜索结果页用
useSeo({ robots: 'private' }) // 等价 'noindex, nofollow',登录 / 个人中心 / 后台用
useSeo({ robots: 'noindex, nofollow, noarchive' }) // 原始字符串(高级用法)
⚠️ 不要给同一私有 path 再加
robots.txtDisallow:robots.txt 拦掉抓取后,爬虫读不到 page 上的noindex,URL 反而可能被"无 snippet"索引。要 deindex 就保持可抓取 +noindex;纯不想被爬、不在乎索引的路径(/api/等)才用 robots.txtDisallow。详见 framework SEO 指南。
四、HTML 语义化:标签选对就是一半 SEO
4.1 HTML5 Landmark 元素
W3C WCAG H101 强制要求用语义元素标识页面区域。
<body>
<header>
<nav>...</nav>
</header>
<main>
<article>
<h1>页面主标题</h1>
<section>
<h2>章节</h2>
</section>
</article>
<aside>侧栏</aside>
</main>
<footer>...</footer>
</body>
| 元素 | 用途 |
|---|---|
<header> | 页面 / 区域顶部(logo、导航) |
<nav> | 主要导航链接组 |
<main> | 主内容区(每页一个) |
<article> | 独立完整的内容(一篇文章、一个商品) |
<section> | 主题相关的内容分组(带 heading) |
<aside> | 与主内容相关但可独立的内容(侧栏) |
<footer> | 页面 / 区域底部 |
爬虫和屏幕阅读器都用这些 landmark 理解页面结构。<div> 嵌一堆没语义的层级 = 让爬虫和屏幕阅读器懵逼。
4.2 H1–H6 标题层级
铁律:每页只一个 <h1>(页面主标题),且是视觉与语义最重要的那个。
层级清晰、不跳级:
- ✅
h1→h2→h3→h2→h3 - ❌
h1→h3→h2(中间跳过 h2) - ❌ 把
<h3>当样式用("我想要小一点的字"),应该用class控制视觉
业务实践:
- 列表页:
<h1>是分类名(如 "动漫");列表项卡片用<h2>或<h3> - 详情页:
<h1>是作品名;章节用<h2> - 首页:
<h1>可以是站名或主 slogan
4.3 不要 <div> 滥用
<!-- ❌ -->
<div class="header">
<div class="nav">
<div class="link">首页</div>
</div>
</div>
<!-- ✅ -->
<header>
<nav>
<NuxtLink to="/">首页</NuxtLink>
</nav>
</header>
工具:axe DevTools 浏览器插件可以扫语义问题。
五、链接:ahref 是爬虫的命脉
5.1 NuxtLink / SwLink,不要 onClick + router.push
爬虫只跟 <a href="...">。@click + router.push() 跳转的链接对搜索引擎完全不可达。
<!-- ❌ 爬虫看不到这个链接 -->
<button @click="$router.push('/movie/123')">详情</button>
<!-- ✅ 爬虫可达,且支持 prefetch / 中键打开 / 用户右键复制链接 -->
<NuxtLink to="/movie/123">详情</NuxtLink>
<!-- ✅ Swift Web 推荐:SwLink 内置爬虫语义 + 视觉样式 -->
<SwLink to="/movie/123">详情</SwLink>
例外:纯 UI 操作(展开折叠、切换 tab)才用 button + onClick。任何路由跳转必须 a 标签。
5.2 Anchor text(锚文本)要描述性
<!-- ❌ -->
<NuxtLink to="/article/seo">点击这里</NuxtLink>
<NuxtLink to="/article/seo">了解更多</NuxtLink>
<!-- ✅ -->
<NuxtLink to="/article/seo">SEO 优化技巧详解</NuxtLink>
爬虫用 anchor text 理解目标页面主题,"点击这里" 等于浪费一次排名机会。
5.3 内链结构 = 网站地图
- 重要页面要有多入口(首页、分类、相关推荐、面包屑都指向)
- 次要页面(私有 / 工具页)不要在主流量路径放链接
- 死链 / 404 链接要清理(爬虫预算浪费)
5.4 外链:rel 属性
<!-- 不要给外链投票(避免被低质站拖累) -->
<a href="https://other.com" rel="nofollow">参考资料</a>
<!-- 用户生成内容里的外链:sponsored / ugc -->
<a href="https://ad.com" rel="sponsored">广告链接</a>
<a href="https://example.com" rel="ugc">用户分享的链接</a>
<!-- 在新窗口打开的外链:noopener 防止 tab 劫持 + noreferrer 不传 referer -->
<a href="https://other.com" target="_blank" rel="noopener noreferrer">外部站点</a>
六、可访问性 WCAG 2.2 = SEO 加成
W3C WCAG 2.2 是 2026 实战标准。可访问性与 SEO 共享同一基础:屏幕阅读器和搜索引擎爬虫看的是同一份语义。
6.1 图片 alt(必填)
<!-- ❌ -->
<img src="/cover.jpg">
<!-- ✅ -->
<img src="/cover.jpg" alt="《某动漫》第 1 集封面,主角持剑站立">
<!-- ✅ Swift Web 推荐:SwImage 强制 alt 参数 -->
<SwImage src="/cover.jpg" alt="..." :ratio="16/9" />
alt 描述图片内容/语义,不是文件名。装饰性图片用 alt="" 表示"忽略我",不要省略 attribute。
6.2 表单 label 关联
<!-- ❌ placeholder 不能替代 label -->
<input type="email" placeholder="邮箱">
<!-- ✅ -->
<label for="email">邮箱</label>
<input id="email" type="email" placeholder="user@example.com">
<!-- ✅ 包裹式 -->
<label>
邮箱
<input type="email">
</label>
6.3 ARIA:补充 HTML 缺失的语义
ARIA 不是替代 HTML 语义元素,而是当 HTML 没合适标签时补。
<!-- ✅ 切换按钮的状态 -->
<button aria-expanded="true" aria-controls="menu">菜单</button>
<ul id="menu">...</ul>
<!-- ✅ 自定义对话框 -->
<div role="dialog" aria-labelledby="dlg-title" aria-modal="true">
<h2 id="dlg-title">确认删除</h2>
...
</div>
6.4 键盘导航 + Touch Target
WCAG 2.2 新增:
- 键盘可见焦点(focus indicator 不能靠 hover)
- Touch target ≥ 24×24 px(移动端)—— 实战推荐 ≥ 44×44 px
- 重复输入(用户不应被要求重复输入相同信息)
- 可访问的认证(不要强制记忆类验证)
6.5 颜色对比
正文文本对比度 ≥ 4.5:1;大文本(≥18px 或 ≥14px 加粗)≥ 3:1。工具:WebAIM Contrast Checker。
七、URL / 路由 / canonical
7.1 语义化路径
✅ /movie/spirited-away
✅ /movie/123-spirited-away (id + slug 双保险)
❌ /detail?id=123 (query 参数不语义化)
❌ /m/123 (太精简,无信息)
URL 路径中的关键词是轻量级排名信号。爬虫和用户都能从 URL 看出页面主题。
7.2 重要内容用 path,过滤参数用 query
/movie/list 主资源(被索引)
/movie/list?genre=action 过滤参数(不一定每个组合都被索引)
/movie/list?page=2 分页(rel="next"/"prev")
爬虫预算(crawl budget)有限,无意义的 query 组合会浪费爬虫资源。配合 robots.txt 的 Disallow: /*?utm_* 屏蔽营销参数 URL 被索引。
7.3 canonical:规范化重复内容
同一内容多个 URL(多副域名 / 大小写 / 结尾斜杠 / 营销参数)会被搜索引擎判定为 duplicate content。<link rel="canonical"> 告诉搜索引擎"以这个 URL 为准"。
// useSeo 自动写 canonical
useSeo({
title: '...',
description: '...',
// 默认 canonical = `${siteUrl}${currentPathname}`
canonicalPath: '/movie/123', // 显式覆盖(罕见)
siteUrl: 'https://main.example.com', // 多副域名场景显式覆盖
})
7.4 避免动态参数过深
❌ /shop/cat/sub/sub/sub/item/123 太深,权重稀释
✅ /shop/item/123 扁平结构
八、结构化数据 Schema.org / JSON-LD
Schema.org 是 Google / Bing / Yandex 共建的结构化词汇表,2026 含 823 类型 / 1529 属性。让搜索引擎和 AI 答案引擎"读懂"页面结构 → 触发富结果(Rich Results) 与 AI Overview 引用。
8.1 JSON-LD 是 Google 推荐格式
三种格式:JSON-LD / Microdata / RDFa。Google 明确推荐 JSON-LD:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "SEO 优化技巧",
"author": { "@type": "Person", "name": "张三" },
"datePublished": "2026-04-28"
}
</script>
JSON-LD 与 DOM 解耦,便于模板化、易于 AI 抓取。
8.2 useSeo 自动输出
Swift Web useSeo 默认输出 WebSite + WebPage 两个 schema 节点:
useSeo({
title: '...',
description: '...',
})
// 等价输出:
// {
// "@context": "https://schema.org",
// "@graph": [
// { "@type": "WebSite", "name": "...", "url": "..." },
// { "@type": "WebPage", "name": "...", "description": "..." }
// ]
// }
详情页传 type: 'article' 即输出 Article schema(article 字段可选,但缺 datePublished 时 schema 残缺、富摘要不识别,务必一并传):
useSeo({
title: data.value?.name,
description: data.value?.summary,
image: data.value?.cover,
type: 'article',
article: {
author: data.value?.author,
datePublished: data.value?.created_at,
dateModified: data.value?.updated_at,
},
})
8.3 常用 Schema 类型
| 类型 | 适用 | Swift Web 实现 |
|---|---|---|
| WebSite | 站点级(一站一份) | useSeo 自动 |
| WebPage | 页面级(每页一份) | useSeo 自动 |
| Article / NewsArticle / BlogPosting | 文章 / 新闻 / 博客 | useSeo({ type: 'article' }) |
| BreadcrumbList | 面包屑导航 | 待加(建议组件级) |
| FAQ | FAQ 页 | 业务自实现 schema-org/vue 调用 |
| Product | 商品 / 应用 | 业务自实现 |
| Organization | 机构信息 | 业务自实现(首页) |
8.4 富结果触发条件
Google 富结果的展示由算法决定(不保证),但必要条件:
- 必填字段齐全(每种 type 不同,Rich Results Test 验证)
- 数据真实(与页面可见内容一致,不能藏起来)
- 页面被索引
8.5 GEO 维度:让 AI 引用
JSON-LD 是 AI 答案引擎抽取事实的主要数据源。结构化越清晰,被 grounding 的概率越高。
九、Core Web Vitals 2026(最影响排名)
Core Web Vitals 是 Google 公开的用户体验排名信号,Google Search 用 75% 真实用户数据评估(不是实验室数据)。
9.1 三大指标 + 阈值
| 指标 | Good | Needs Improvement | Poor |
|---|---|---|---|
| LCP(最大内容渲染) | ≤ 2.5s | 2.5–4.0s | > 4.0s |
| INP(交互响应延迟) | ≤ 200ms | 200–500ms | > 500ms |
| CLS(累计布局偏移) | ≤ 0.1 | 0.1–0.25 | > 0.25 |
INP 在 2024 年取代 FID,更全面反映交互流畅度。所有 click / tap / keypress 后的下一帧延迟都被测量,p98 即为 INP。
9.2 LCP 优化(最大内容渲染)
LCP 元素通常是首屏的大图或大段文本。优化方向:
<!-- ✅ Swift Web 实践:首屏 hero 图 -->
<SwImage
src="/hero.jpg"
:ratio="16/9"
alt="..."
preload <!-- 加 <link rel="preload"> -->
fetchpriority="high" <!-- 提升优先级 -->
loading="eager" <!-- 不懒加载 -->
/>
服务端:
- 用 ISR / SSR 而不是 CSR(HTML 预渲染减少 LCP)
- CDN 缓存静态资源
- 字体子集化 +
font-display: swap
9.3 INP 优化(交互响应延迟)
<!-- ✅ Swift Web 实践:SwButton + :loading 状态机防 double-submit -->
<SwButton :loading="submitting" @click="handleSubmit">提交</SwButton>
<!-- :loading 状态机父组件管,async 期间禁用按钮 -->
代码层:
- 避免 main thread 长任务(> 50ms 的 JS 计算)—— 重计算用
Web Worker - 用
requestIdleCallback调度非紧急任务 - 减少同步 React/Vue 重渲染(精确 reactivity)
9.4 CLS 优化(累计布局偏移)
布局偏移来源:
- 图片 / 视频 / 广告位没占位,加载完才撑开
- 字体 swap 导致文字宽高变化(FOIT/FOUT)
- 异步插入的内容(toast、modal)顶开主内容
<!-- ✅ Swift Web 实践:SwImage 强制 ratio -->
<SwImage src="..." :ratio="16/9" />
<!-- 占位框先撑开 16:9 容器,图片加载完无偏移 -->
字体:
/* ✅ font-display: swap + size-adjust 微调 */
@font-face {
font-family: 'BrandFont';
src: url('/font.woff2');
font-display: swap;
size-adjust: 100%;
ascent-override: 90%;
}
9.5 监控
- Search Console Core Web Vitals 报告(真实用户数据,p75)
- PageSpeed Insights(实验室 + 真实用户)
- CrUX Dashboard(Chrome User Experience Report)
- 业务 RUM(Real User Monitoring):可选自建
十、sitemap.xml / robots.txt
10.1 sitemap.xml:告诉爬虫有哪些页
Swift Web 用 @nuxtjs/sitemap(在 framework 里默认启用)自动生成 /sitemap.xml:
- 列出所有可索引的 URL(
swiftConfig.seo.sitemap.exclude追加排除私有 path;framework 已默认排除/login、/mine/**等通用私有路径) - 包含
<lastmod>时间戳,加速增量爬取 - 多语言 hreflang 互链(
<xhtml:link rel="alternate" hreflang="...">)规划中(framework#88,等真实 prefix strategy applier 落地后启动)
提交 sitemap:在 Google Search Console 和 Bing Webmaster Tools 各自提交一次(一次性配置)。
10.2 robots.txt:爬虫第一站
# /public/robots.txt(或由 @nuxtjs/robots 动态生成)
User-agent: *
Disallow: /api/
Disallow: /*?utm_*
Allow: /
Sitemap: https://example.com/sitemap.xml
字段含义:
User-agent: *— 适用所有爬虫Disallow: /api/— 禁止爬 API 接口路径Disallow: /*?utm_*— 屏蔽营销参数 URLSitemap:— 指向 sitemap.xml(可多个)
⚠️
Disallow与 metanoindex不要叠加在同一路径:robots.txt 拦抓取 → 爬虫看不到noindex→ 可能"无 snippet"索引。按目标二选一:要 deindex / 排除私有页用 page metarobots: 'private'(保持可抓取);纯屏蔽抓取(/api/、utm 参数)用 robots.txtDisallow。
10.3 AI 爬虫策略
2024 起 GPTBot / ClaudeBot / Google-Extended / PerplexityBot 等 AI 爬虫陆续出现。Swift Web seo.aiBotPolicy 选项控制:
// nuxt.config.ts → @nuxtjs/robots
swiftConfig: {
seo: { aiBotPolicy: 'allow' | 'disallow' }
}
// disallow: 在 robots.txt 加 User-agent: GPTBot/ClaudeBot/... + Disallow: /
业务决策:
- 内容希望被 AI 引用(增加曝光)→ allow
- 付费 / 内部内容 → disallow + meta noarchive
十一、Open Graph / Twitter Card / 社交分享
11.1 og:* 让分享更精美
Facebook / LinkedIn / 微信 / Slack / Discord 等共用 Open Graph 协议。Twitter 用 Twitter Cards(语义类似)。
// useSeo 自动写完整 og:* 与 twitter:*
useSeo({
title: '...', // → og:title, twitter:title
description: '...', // → og:description, twitter:description
image: '/cover.jpg', // → og:image, twitter:image
type: 'article', // → og:type
locale: 'zh_CN', // → og:locale
})
// 自动加:og:url, twitter:card='summary_large_image'
// og:image:width=1200, og:image:height=630
// 站点名不走 og:site_name —— 进 WebSite JSON-LD(取 runtimeConfig.public.siteName)
11.2 og:image 标准尺寸
1200 × 630 px(1.91:1)是 Facebook / Twitter / LinkedIn 通用标准;最小 200 × 200。
Swift Web 兜底链:input.image → /og-image.png(项目级 prebuild 强制存在;#89 起不再读后端 header.og_image / header.img)。
11.3 调试工具
- Facebook 分享调试工具
- Twitter Card Validator
- LinkedIn Post Inspector
- 微信:开发者工具内置 OG 检测(开发者网页)
十二、国际化 / hreflang
多语言站(zh-CN / en / zh-TW)用 hreflang 告诉搜索引擎:"我有这页的不同语言版本",避免被判定为 duplicate content。
12.1 hreflang 标注
<link rel="alternate" hreflang="zh-CN" href="https://example.com/zh/movie/1">
<link rel="alternate" hreflang="en" href="https://example.com/en/movie/1">
<link rel="alternate" hreflang="zh-TW" href="https://example.com/tw/movie/1">
<link rel="alternate" hreflang="x-default" href="https://example.com/movie/1">
x-default 给"语言不匹配时的默认版本"。
12.2 Swift Web 实践
@nuxtjs/i18n + useSeo 自动处理:
// nuxt.config.ts
swiftConfig: {
i18n: {
enabled: true,
defaultLocale: 'zh-CN',
locales: ['zh-CN', 'en', 'zh-TW'],
},
}
// → hreflang 自动配置规划中(framework#88,等真实 prefix strategy applier 落地后启动)
// → og:locale 需页面显式传 useSeo({ locale }),不会自动取当前 i18n locale
12.3 i18n strategy 必填,禁用 no_prefix(强约束)
启用 @nuxtjs/i18n 时必须用 strategy: 'prefix' 或 'prefix_except_default',禁用 'no_prefix':
'no_prefix'让三语共用同一 URL(/about既是 zh-CN 又是 en)→ sitemap.xml 无法输出<xhtml:link rel="alternate" hreflang>互链 → 搜索引擎判 duplicate content → 多语言 SEO 归零。- framework 在 dev 期检测到
no_prefix且 locales ≥ 2 会console.warn,提示改用prefix_except_default。 - 仅当业务明确不在乎多语言 SEO(纯内部系统 / 登录态后台)才允许
no_prefix。
i18n: {
strategy: 'prefix_except_default', // 默认语言无前缀,其它语言带 /en/、/zh-TW/ 前缀
locales: [
{ code: 'zh-CN', file: 'zh-CN.json' },
{ code: 'zh-TW', file: 'zh-TW.json' },
{ code: 'en', file: 'en.json' },
],
}
12.4 注意事项
- 每个语言版本必须互相引用(self-referential 也要写)
- canonical 不跨语言(每个语言各自 canonical 自己)
- URL 策略:path prefix(/zh/...)优于 query(?lang=zh),优于子域名(zh.example.com)
- 避免机翻 SEO 灾难:低质机翻被算法识别为低价值内容
附录 A:MR Review SEO 自检清单
每个 page / component PR 提交前,开发者自查 + reviewer 重点检查项:
必查(必改)
-
app/pages/**/*.vue和app/error.vue是否有useSeo()调用?(ESLintswift-web/require-useSeo-in-pages兜底,fw#240 起覆盖 error.vue) - TDK 长度达标?description 中文 25-80 字 / 英文 50-160 字符,title 中文 5-30 字(字面量由 ESLint
swift-web/seo-tdk-length静态预检;工具/私有页改robots: 'private'+silenceLengthWarnings: true) - 私有页(login / register / mine / profile/* / admin / demo / creator)是否标
robots: 'private'? - 中转 / 重定向页 / 站内搜索结果页是否标
robots: 'noindex'? - 私有页是否加
silenceLengthWarnings: true(避免 dev console 噪音)? - 详情页是否传
image(用data?.cover,不用站点默认占位)? - 详情页是否走后端 SEO 兜底链(
data?.seo?.title ?? data?.name)? - 详情页是否设
type: 'article'+article字段? - 渲染策略是否合理(
defineRouteRules强制声明,详情页 isr / 内容页不用 csr)?
重要(讨论)
- 路由跳转是否用
<NuxtLink>/<SwLink>,没有@click + router.push? - 图片是否用
<SwImage>,包含alt+ratio,首屏图加preload+fetchpriority="high"? - 表单
<input>是否有关联的<label>? - 页面只有一个
<h1>?层级 h1→h2→h3 不跳级? - 用语义元素(
<header>/<nav>/<main>/<article>/<section>/<footer>)而非<div>堆? - 外链有
rel="nofollow"/"noopener noreferrer"?
次要(提及)
- anchor text 是否描述性(不是"点这里")?
- URL 路径是否语义化(
/movie/123而不是/detail?id=123)? - 颜色对比度满足 WCAG(正文 ≥4.5:1,大字 ≥3:1)?
Comment 模板
必改 / 重要:用 GitLab Discussion(带 position 关联代码行)
次要:review 总结里提一嘴,不开 Discussion
附录 B:权威参考资料
- Google Search Central 文档主页
- SEO Starter Guide
- Search Quality Rater Guidelines(PDF)
- Core Web Vitals / LCP / INP / CLS
- Rich Results Test
- PageSpeed Insights
Bing
Schema.org
W3C / WCAG
- WCAG 2.2 — 现行可访问性标准
- H101 语义元素技术
- WAI-ARIA 1.3
工具
- axe DevTools — 可访问性扫描
- WebAIM Contrast Checker — 颜色对比检测
- Facebook 分享调试工具
- Twitter Card Validator
Swift Web 内部
framework/CLAUDE.mdSEO 章节- standards 仓
rulesSEO Review 规则 - useSeo.ts JSDoc