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 长度建议来自哪里

字段软建议硬上限来源
title10–60 显示宽度> 70 触发关键词堆砌Google SERP ~60、Bing ~65 截断
description50–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.txt Disallow:robots.txt 拦掉抓取后,爬虫读不到 page 上的 noindex,URL 反而可能被"无 snippet"索引。要 deindex 就保持可抓取 + noindex;纯不想被爬、不在乎索引的路径(/api/ 等)才用 robots.txt Disallow。详见 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 是爬虫的命脉

爬虫只跟 <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面包屑导航待加(建议组件级)
FAQFAQ 页业务自实现 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 三大指标 + 阈值

指标GoodNeeds ImprovementPoor
LCP(最大内容渲染)≤ 2.5s2.5–4.0s> 4.0s
INP(交互响应延迟)≤ 200ms200–500ms> 500ms
CLS(累计布局偏移)≤ 0.10.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_* — 屏蔽营销参数 URL
  • Sitemap: — 指向 sitemap.xml(可多个)

⚠️ Disallow 与 meta noindex 不要叠加在同一路径:robots.txt 拦抓取 → 爬虫看不到 noindex → 可能"无 snippet"索引。按目标二选一:要 deindex / 排除私有页用 page meta robots: 'private'(保持可抓取);纯屏蔽抓取(/api/、utm 参数)用 robots.txt Disallow。

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 调试工具


十二、国际化 / 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() 调用?(ESLint swift-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

Bing

Schema.org

W3C / WCAG

工具

Swift Web 内部