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_TW',        // → og:locale
})
// 自動加:og:url, twitter:card='summary_large_image'
// 站點名不走 og:site_name —— 進 WebSite JSON-LD(取 runtimeConfig.public.siteName)
// og:image:width=1200, og:image:height=630

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 內部