1038 字
5 分鐘

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:

src/content.config.ts
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 或大型資料集效能。

建置前先做三個檢查#

  1. 在 base 建立一個最小範例,確認 pattern 真的包含它。
  2. 故意少填 schema 必填欄位,確認錯誤能指出正確 entry。
  3. 執行 pnpm check 後再跑完整建置,避免只靠型別提示漏掉 loader 的資料問題。

這個專案既有文章仍可透過 Content Collections 提供給頁面與相關文章計算;若你要處理文章間的連結排序,可再參考 Astro 部落格的自動延伸閱讀。glob() 的工作是定義輸入集合,不是替內容關聯打分數。

參考資料:

Astro Docs:Content Loader API

Astro Docs:Content collections

Astro Content Loader 怎麼用:用 glob() 建立可驗證的本機內容集合
https://laplusda.com/posts/astro-content-loader-glob-collection/
作者
Zero
發佈於
2026-08-02
許可協議
CC BY-NC-SA 4.0