開發紀錄
首頁、列表、文章頁與 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 或讀者成效的保證。