skip to content
鰭狀漏斗

初探 Astro Content Layer

/ 閱讀時間 4 分鐘

上次更新:

Astro 4.14 引進了新的功能:Content Layer,加強了原有的 Content Collection。原本的 Content Collection 資料只能放在 src/content 資料夾裡面,新的 Content Layer 則是沒有這個限制了,可以想放在哪個路徑就放在哪個路徑,即使資料來源在網路上也 OK。我們接下來就看看它的用法吧。

啟用 Content Layer

首先因為 Content Layer 還是實驗功能,所以要在 Astro config 啟用:

astro.config.js
import { defineConfig } from "astro";
export default defineConfig({
experimental: {
contentLayer: true
}
});

Loader

在舊有的 Content Collection,定義可能長這樣子:

src/content/config.js
import { defineCollection, z } from "astro:content";
const blog = defineCollection({
type: "content",
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
heroImage: z.string().optional()
})
});
export const collections = { blog };

要使用新的 Content Layer,我們把 type 去掉,加上一個 loader

src/content/config.js
import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders";
const blog = defineCollection({
loader: glob({ pattern: "**/*.md", base: "path/to/blog" }),
schema: z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
heroImage: z.string().optional()
})
});
export const collections = { blog };

這個 loader 是 Content Layer 的重點,現在在 Astro 有兩個內建的 loaderglobfile。你可以用 glob loader 存取本地的資料,這個資料不一定要在 src/content 底下,或者可以用 file loader 存取包含很多筆資料的單一檔案。

除了使用內建的 loader 外,也可以自己做一個 loader 獲得不知道哪來的資料,像 NotWoods 就做了一個 Notion Loader,可以把 Notion 的頁面當做資料來源。

跟 Content Collection 的差別

除了剛剛提到 Content Layer 的資料放置位置沒有 Content Collection 的限制外,Content Layer 跟 Content Collection 還有一些差別,使得要改用 Content Layer 需要改很多東西。

Slug vs ID

typecontent 的 Content Collection 中有一個特殊的 property:slug,它預設從 file id 產生,也可以自己另外設定。在一個 Content Collection 中每個 entry 的 slug 都是獨一無二的,可以用 slug 經由 getEntry() 取得它所代表的 entry。

但是在 Content Layer slug 被合併成 id 了,所以如果是 Markdown 檔案的 Content Collection 要改成 Content Layer 的話,要把所有 slug 替換成 id,除了 Markdown 檔案 frontmatter 中的 slug

Markdown 內容的 render()

原本 Content Collection 中將內容 render 成 HTML 的 render() 是 entry 的 function property:

import { getEntry } from "astro:content";
const entry = await getEntry("blog", "post-1");
const { Content, headings } = await entry.render();

在 Content Layer 裡獨立出來了,需要另外 import:

import { getEntry, render } from "astro:content";
const entry = await getEntry("blog", "post-1");
const { Content, headings } = await render(entry);

結論

Content Layer 比 Content Collection 還要強大,可以自製的 loader 讓內容不只侷限在 src/content 中的檔案。但是要將 Markdown Collection 改成 Content Layer 會是一個大工程。

要詳細了解 Content Layer 可以參考: