blocks 欄位儲存一個有序的頁面組合。每一項都會記錄區塊類型、保留的 schema 版本、穩定的 key,以及該版本所宣告的欄位。編輯者在內容編輯器中新增區塊並調整順序。Astro 路由會把每種區塊類型對應到一個元件。
定義區塊類型
請在使用區塊類型的集合欄位之前先定義區塊類型。種子會保留每一個編號版本以及目前生效的 currentVersion 指標。
下面的種子定義了 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。該值包含 _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.rewrite("/404");
const { entry: page, cacheHint } = await getEmDashEntry("pages", slug);
if (!page) return Astro.rewrite("/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 欄位本身不能設為必填、唯一、可搜尋、可索引,也不能指定自訂欄位小工具。
有關確切的欄位校驗和已儲存值的規則,請參閱 blocks 欄位參考。有關種子衝突和匯出行為,請參閱 Seed 檔案。