跳至主要內容

開發紀錄

首頁、列表、文章頁與 sitemap 為什麼要共用發布查詢

從 `getPublishedNotes` 的實際使用點,說明首頁、筆記列表、文章詳頁、分頁與 sitemap 如何共用同一套公開資格判斷,再各自處理呈現方式。

林予晴
發布
  • 內容工程
  • Astro
  • Content Collections
  • 發布查詢

文章是否公開,不能由首頁、列表、文章頁和 sitemap 各自決定。只要其中一個地方多放了一篇草稿,或少放了一篇正式文章,讀者看到的網站就會出現互相矛盾的狀態。

目前的內容系統把「哪些文章有資格公開」集中在 getPublishedNotes(),再讓不同頁面各自決定怎麼呈現。主要判斷是:公開資格應該只有一份來源,頁面差異留在查詢之後處理,而不是每個入口各寫一套過濾規則。

先集中定義什麼叫可發布

目前的資料流可以簡化成:

Markdown
  → getAllNotes()
  → validateNoteIdentities()
  → selectPublishedNotes()
  → getPublishedNotes(locale?)
  → 首頁/列表/文章頁/sitemap/延伸閱讀

getAllNotes() 先檢查文章身份、目錄語言和正文標題階層。接著 getPublishedNotes() 以台北時區取得今天的日期,交給 selectPublishedNotes() 判斷公開資格。

一篇文章必須同時符合以下條件:

  • draft 是 false。
  • 有 publishedAt。
  • publishedAt 不晚於今天。
  • 如果查詢指定語言,locale 必須相符。

通過後,集合依發布時間由新到舊排序;只有秒級時間相同時才依 stableId 排序。這些規則集中在 src/utils/notes-model.ts,不是散落在各個 Astro 頁面的條件式裡。

集中判斷還有一個實際好處:所有頁面面對同一篇文章時,會使用同一個台北日期、同一個草稿條件和同一個排序規則。要改發布契約時,也能先改一個內容模型,再重新跑所有輸出檢查。

同一份公開集合,會被不同頁面使用

共用查詢不代表所有頁面要長得一樣。它只先回答「哪些文章可以被看見」,之後各入口再依自己的責任取用集合:

使用點 取用方式 頁面自己的責任
首頁 getPublishedNotes(lang).slice(0, 3) 只放該語言最新最多三篇,並提供完整列表入口
/notes getPublishedNotes(lang) 呈現完整列表、空狀態與列表內的分組/分頁
/notes/page/[page] getPublishedNotes() 加 paginateNotes() 只建立實際需要的後續列表頁
/notes/[slug] getPublishedNotes() 的 getStaticPaths() 只產生正式繁中文章詳頁
/en/notes/[slug]、/ja/notes/[slug] 同一查詢後依語言篩選 只產生實際存在且可發布的翻譯文章
sitemap getPublishedNotes() 只輸出正式文章 URL 與實際存在的列表頁
文章延伸閱讀 同語言的 getPublishedNotes() 在公開集合中排除目前文章,再依 metadata 排序

因此首頁可以只顯示三篇,列表可以顯示更多,文章頁可以產生單篇路由,sitemap 可以列出所有正式文章。這些是不同的呈現責任,不是不同的發布規則。

如果每頁自己過濾,問題會藏在輸出裡

假設首頁只檢查 draft,卻沒有檢查未來的 publishedAt,一篇排程中的文章可能先出現在首頁。假設 sitemap 只掃描所有 Markdown,草稿就可能有 sitemap URL,即使列表沒有卡片。假設文章詳頁沒有使用同一個集合,讀者從某個連結進去時還可能看到列表裡不存在的文章。

這些錯誤不一定會在 TypeScript 或單頁 build 時出現,因為每個頁面自己的條件式都可能是合法程式。問題在於不同入口對「正式文章」的理解不一樣。

把公開查詢集中後,頁面只需要處理自己的輸出:首頁處理數量,列表處理排列,文章頁處理正文,sitemap 處理 URL。它們不必再次複製 draft、日期與語言的判斷。

測試要同時看模型和產物

目前的筆記測試先測內容模型本身,包括:

  • 草稿可以沒有 publishedAt。
  • 正式文章沒有 publishedAt 時會失敗。
  • 無效日期與較早的 updatedAt 會被拒絕。
  • 草稿與未到日期文章不會出現在公開查詢。
  • 同一秒文章依 stableId 維持穩定排序。
  • 語言篩選與文章 URL 使用同一套身份規則。
  • 預覽路由只在 Astro dev 註冊。

verify:notes 再讀回實際建置產物,檢查 source Markdown、dist/、sitemap、文章頁和三語首頁的數量與連結是否一致。這兩層不能互相取代:模型測試能確認條件,輸出驗證才能確認這些條件真的影響了頁面和 sitemap。

共用發布查詢仍然不是內容品質檢查

getPublishedNotes() 能回答文章是否符合目前的發布條件,不能回答文章內容是否寫得好,也不能證明日期、標題或描述適合公開。它同樣不能證明 SEO 排名、讀者理解、流量或合作結果。

它還有一個靜態網站的限制:未來日期的文章不會自己在午夜上線。文章必須在符合日期後重新建置,新的公開集合才會被寫進首頁、列表、文章頁和 sitemap。

這也是為什麼發布查詢應該保持單純。它負責一個窄問題:哪些內容可以進入正式輸出。內容真實性、公開範圍、視覺閱讀與發布授權,仍然是另外的檢查。

文章增加後,先追使用點再改規則

要修改筆記發布流程時,可以先沿著 getPublishedNotes 的使用點檢查:首頁、列表、分頁、文章詳頁、延伸閱讀和 sitemap 是否都仍然使用同一份公開集合,再看各頁是否只做自己的呈現處理。

這比在某一頁補一個條件式可靠,因為發布規則的問題通常不是單一畫面錯了,而是同一篇文章在不同出口被判定成不同狀態。公開資格只有一份,頁面才不會各自說一種答案。

紀錄範圍

本文根據 getAllNotes、getPublishedNotes、selectPublishedNotes 的實作,及首頁、列表、分頁、文章路由、sitemap、文章版型與筆記驗證腳本整理。它描述目前的內容查詢契約,不把共用查詢誤寫成內容品質、SEO 或讀者成效的保證。