blocks 欄位儲存一個有序的頁面組合。每個項目都記錄了區塊類型、保留的 schema 版本、一個穩定的鍵,以及該版本宣告的欄位。編輯者在內容編輯器中新增區塊並調整其順序。Astro 路由將每種區塊類型對應到一個元件。
定義區塊類型
先定義區塊類型,再定義使用它們的集合欄位。seed 會保留每個帶編號的版本,以及目前生效的 currentVersion 指標。
以下 seed 定義了 Hero 與 Feature grid 兩種區塊,然後讓它們可以在 Pages 集合的 layout 欄位中使用:
{
"$schema": "https://emdashcms.com/seed.schema.json",
"version": "1",
"blockTypes": [
{
"slug": "hero",
"label": "Hero",
"category": "Layout",
"currentVersion": 1,
"versions": [
{
"version": 1,
"fields": [
{ "slug": "heading", "label": "Heading", "type": "string", "required": true },
{ "slug": "body", "label": "Body", "type": "portableText" },
{ "slug": "image", "label": "Image", "type": "image" },
{ "slug": "link_label", "label": "Link label", "type": "string" },
{ "slug": "link_url", "label": "Link URL", "type": "url" }
]
}
]
},
{
"slug": "feature_grid",
"label": "Feature grid",
"category": "Layout",
"currentVersion": 1,
"versions": [
{
"version": 1,
"fields": [
{ "slug": "heading", "label": "Heading", "type": "string" },
{
"slug": "items",
"label": "Items",
"type": "repeater",
"validation": {
"subFields": [
{ "slug": "title", "label": "Title", "type": "string", "required": true },
{ "slug": "description", "label": "Description", "type": "text" }
]
}
}
]
}
]
}
],
"collections": [
{
"slug": "pages",
"label": "Pages",
"fields": [
{ "slug": "title", "label": "Title", "type": "string", "required": true },
{
"slug": "layout",
"label": "Layout",
"type": "blocks",
"validation": {
"allowedTypes": ["hero", "feature_grid"],
"maxItems": 20
}
}
]
}
]
}
allowedTypes 的順序決定了區塊選擇器中的排列順序。如果之後移除了某個允許的類型,EmDash 會將它移到由伺服器管理的 retiredTypes 清單中。既有的區塊仍然可以編輯,但編輯者無法再新增或複製該類型的區塊。
建立 Astro 元件
每個元件都會接收 value、index 與 blockKey。value 中包含 _type、_version 與 _key,因此當區塊 schema 演進時,元件可以據此區分保留的各個版本。
Hero 元件從已儲存的區塊中讀取所有要顯示的值:
---
import { sanitizeHref } from "emdash";
import { Image, PortableText, type BlockComponentProps } from "emdash/ui";
import type { PageLayoutBlock } from "../../../emdash-env";
type HeroBlock = Extract<PageLayoutBlock, { _type: "hero" }>;
type Props = BlockComponentProps<HeroBlock>;
const { value } = Astro.props;
---
<section class="hero">
<div>
<h1>{value.heading}</h1>
{value.body && <PortableText value={value.body} />}
{value.link_url && <a href={sanitizeHref(value.link_url)}>{value.link_label}</a>}
</div>
{value.image && <Image image={value.image} />}
</section>
為每種允許的類型建立一個元件。元件負責標記與樣式;區塊的值提供內容與媒體。
呈現頁面組合
使用 defineBlockComponents 可以要求為產生的欄位聯合型別中的每個 _type 都提供一個元件。在頁面路由中將這個對應表傳給 <Blocks>。
---
import { decodeSlug, getEmDashEntry } from "emdash";
import { Blocks, defineBlockComponents } from "emdash/ui";
import type { PageLayoutBlock } from "../../../emdash-env";
import FeatureGrid from "../../components/blocks/FeatureGrid.astro";
import Hero from "../../components/blocks/Hero.astro";
const slug = decodeSlug(Astro.params.slug);
if (!slug) return Astro.redirect("/404");
const { entry: page, cacheHint } = await getEmDashEntry("pages", slug);
if (!page) return Astro.redirect("/404");
Astro.cache.set(cacheHint);
const components = defineBlockComponents<PageLayoutBlock>({
hero: Hero,
feature_grid: FeatureGrid,
});
---
<Blocks value={page.data.layout} components={components} />
<Blocks> 不會執行任何內容、schema、媒體或網路查詢。它依儲存順序呈現傳入的陣列。如果某個區塊元件需要其他資料,可以在該元件中明確執行應用程式查詢。
處理缺少的元件
在開發環境中,未對應的類型會顯示一個可見的預留位置,並在主控台輸出警告。預留位置會顯示 _type,但不會印出已儲存的區塊值。
在正式環境中,如果提供了 fallback 元件,未對應的類型會呈現該元件;否則不輸出任何內容:
---
import MissingBlock from "../../components/blocks/MissingBlock.astro";
---
<Blocks value={page.data.layout} components={components} fallback={MissingBlock} />
在啟用或啟動正式內容所使用的區塊類型之前,請先發布呈現器對它的支援。
變更區塊 schema
相容的變更會修訂目前生效的版本。新增選填欄位、新增預設值或放寬驗證,都會維持相同的版本號。已儲存的區塊會在下次寫入時取得預設值。
破壞性變更會建立一個未啟用的版本。刪除欄位、變更欄位類型、新增必填欄位或收緊驗證,都屬於破壞性變更。
-
透過 schema API 或 MCP 建立破壞性版本,並保持其未啟用狀態。
-
更新呈現器,使其同時處理保留的舊版本與新版本,然後部署呈現器。
-
啟用新版本。啟用後,新建立的區塊將使用該版本。
-
使用
migrateBlocks: true明確遷移已儲存的區塊。在變更_version及其版本特定欄位時,保留每個區塊的_key。
舊版本仍可用於修訂版本、草稿、媒體追蹤與已儲存的內容。區塊類型與保留的版本沒有永久刪除操作。
支援的巢狀欄位
區塊定義支援 string、text、url、number、integer、boolean、datetime、select、multiSelect、portableText、image、file 與 repeater。
區塊定義內不支援參照、JSON、slug、巢狀區塊、自訂元件、實體索引、唯一性限制以及依子欄位在地化。區塊欄位本身不能設為必填、唯一、可搜尋或已建立索引,也不能指定自訂欄位元件。
關於欄位驗證與儲存值的確切規則,請參閱 blocks 欄位參考。關於 seed 衝突與匯出行為,請參閱 Seed 檔案。