Astro Content Loader 怎麼用:用 glob() 建立可驗證的本機內容集合
當文章、設定資料與 JSON 都放進 src/content/ 後,最容易混亂的不是讀取 API,而是「哪些檔案真的屬於這個 collection」。Astro 的 Content Loader API 提供 glob(),可把目錄與副檔名的邊界寫在 collection 定義裡,再交由 schema 在建置期驗證。
核心做法是:用 base 固定資料夾、用窄的 pattern 描述檔案類型、讓 schema 驗證 frontmatter;不要用過寬的 glob 把範例或暫存檔一起收進來。
用 base 把 collection 的責任範圍說清楚
glob() 是 Astro 內建的 object loader,支援 Markdown、MDX、Markdoc、JSON、YAML 與 TOML。以下範例只讀取 src/data/notes 裡的 Markdown:
import { defineCollection } from 'astro:content';import { z } from 'astro/zod';import { glob } from 'astro/loaders';
const notes = defineCollection({ loader: glob({ base: './src/data/notes', pattern: '**/*.md', }), schema: z.object({ title: z.string(), published: z.coerce.date(), }),});
export const collections = { notes };base 是路徑邊界,pattern 則在邊界內篩檔。若集合只該收 Markdown,別寫成 **/*;未來放入圖片、草稿或輔助 JSON 時,建置錯誤才不會變成難以追查的資料型別問題。
ID 不是檔名以外的第二套路徑
預設 ID 由 loader 依 entry 產生。若資料要保留大小寫或要去掉特定副檔名,可用 generateId 明確定義。這個選項適合外部系統 ID 已經存在的 JSON 集合;一般文章 collection 不必先客製化,否則連結與查詢會多一層自行維護的規則。
const authors = defineCollection({ loader: glob({ base: './src/data/authors', pattern: '**/*.json', generateId: ({ entry }) => entry.replace(/\.json$/, ''), }),});retainBody 移除的是原始本文,渲染結果仍可保留
retainBody 預設為 true,從 Astro 5.17.0 加入。依 Content Loader API 文件,設成 false 後,entry.body 會是 undefined;Markdown 已渲染的本文仍可放在 entry.rendered.html,entry.filePath 也保留來源位置。 不該把這個選項解讀成禁止渲染正文。
如果摘要、字數或其他自訂程式直接讀 entry.body,就要保留原始本文,或調整那個功能的資料來源。若頁面只透過 render(entry) 顯示 Markdown,應驗證渲染路徑,而不是只查 body 是否存在。這裡沒有承諾所有 Markdown Plugin、MDX 或自訂 loader 都採相同儲存方式。
Astro 7.1.0 另外加入 deferRender,控制 Markdown 在 content sync 或頁面 render 時才渲染;它與是否保留原始本文是兩個選項。以下驗收固定使用預設的 eager render,沒有開啟 deferRender,也不以單頁測試推論大型站台的時間或空間節省幅度。
用一個隔離專案驗證 body 與畫面
在可丟棄的最小 Astro 7.3.8 專案建立 src/data/notes/hello.md,不要拿正式文章做破壞性驗收:
---title: Hello---
# Visible body
Retained render.將上面的 notes collection 加入 retainBody: false,其他 schema 與 base 維持相同,再建立 src/pages/index.astro:
---import { getCollection, render } from 'astro:content';
const [entry] = await getCollection('notes');if (!entry) throw new Error('Expected a note');if (entry.body !== undefined) throw new Error('Raw body was retained');if (!entry.rendered?.html.includes('Visible body')) { throw new Error('Rendered HTML is missing');}const { Content } = await render(entry);---<html lang="en"> <body><Content /></body></html>在這個測試專案根目錄執行 pnpm build。建置必須通過兩個斷言,輸出的 dist/index.html 也應包含 Visible body 的標題。前者證明 data store 狀態,後者證明頁面實際保留渲染結果;只看到沒有 error,還不足以確認讀者能看到正文。
2026-10-21 維護流程在 macOS、Node.js v24.13.0、Astro 7.3.8 的隔離專案執行 astro build 並檢查輸出,以上三項皆通過。使用的是專案現有的 Astro 安裝,沒有更動正式 collection 或文章來源;未測試 MDX、deferRender、SSR 部署、第三方 Plugin 或大型資料集效能。
建置前先做三個檢查
- 在
base建立一個最小範例,確認 pattern 真的包含它。 - 故意少填 schema 必填欄位,確認錯誤能指出正確 entry。
- 執行
pnpm check後再跑完整建置,避免只靠型別提示漏掉 loader 的資料問題。
這個專案既有文章仍可透過 Content Collections 提供給頁面與相關文章計算;若你要處理文章間的連結排序,可再參考 Astro 部落格的自動延伸閱讀。glob() 的工作是定義輸入集合,不是替內容關聯打分數。
參考資料: