EmDash 為 Astro 站台新增了管理應用程式、由資料庫支援的集合、媒體、選單、分類法、設定、修訂和預覽。頁面和元件仍然是一般的 Astro 檔案。
EmDash 新增了什麼
| 功能 | 提供的能力 |
|---|---|
| 管理後台 | 在 /_emdash/admin 透過瀏覽器管理集合、媒體、選單、分類法和設定 |
| 資料庫集合 | 在請求時查詢的、由編輯者管理的內容 |
| 媒體庫 | 已儲存的圖片和檔案,並為範本提供媒體欄位值 |
| 草稿、修訂和預覽 | 發布之前的編輯工作 |
| 選單和小工具區域 | 位於條目欄位之外的、有序且可編輯的站台區域 |
| 站台設定 | 共用的識別和顯示值,例如標題、標語、標誌和分頁大小 |
| 外掛程式 | 勾點、路由、儲存和選用的管理後台擴充 |
這些功能與 Astro 並存,而不是取代它。路由、版面配置、算繪、樣式和部署介接器仍然由 Astro 控制。
EmDash 和 Astro 集合
Astro 內容集合和 EmDash 集合可以共存。儲存庫所擁有的內容使用 Astro 集合,透過 /_emdash/admin 管理的內容使用 EmDash。
| Astro 內容集合 | EmDash 集合 | |
|---|---|---|
| 儲存 | 專案中的檔案 | SQL 資料庫 |
| 編輯 | 儲存庫工作流程 | EmDash 管理後台 |
| 查詢 | getCollection() | getEmDashCollection() |
| 富文字 | Markdown 或 MDX | Portable Text |
| 傳遞 | 建置時或即時載入器 | 執行階段即時載入器 |
當歸屬不同時,可以同時使用這兩個集合系統。例如,產品站台可以把開發者撰寫的發佈說明保存在 Astro 內容集合中,把編輯者撰寫的文章保存在 EmDash 中:
---
import { getCollection } from "astro:content";
import { getEmDashCollection } from "emdash";
const [releaseNotes, { entries: articles }] = await Promise.all([
getCollection("releases"),
getEmDashCollection("articles", { limit: 3 }),
]);
---
這兩個結果保持獨立。EmDash 不會把以檔案為基礎的條目複製到它的資料庫中。
設定站台
目前的 Node 範本會把 Astro 設定為伺服器端輸出,新增 EmDash 整合,並使用 SQLite 和本機儲存介接器。
下面這份精簡的設定包含了這些必要的部分:
import node from "@astrojs/node";
import react from "@astrojs/react";
import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";
export default defineConfig({
output: "server",
adapter: node({ mode: "standalone" }),
integrations: [
react(),
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
}),
],
});
EmDash 還提供了為 D1 和 R2 設定的 Cloudflare 範本。請從與部署目標對應的範本開始,而不要手工轉換 Node 介接器。
註冊即時集合
範本透過一個名為 _emdash 的 Astro 即時集合來公開 EmDash 內容:
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({ loader: emdashLoader() }),
};
getEmDashCollection() 和 getEmDashEntry() 透過這個載入器來選擇所請求的內容類型。
查詢集合
下面的查詢讀取最近發布的文章。orderBy 使用已儲存的欄位名稱,並把每個名稱對應為 "asc" 或 "desc":
---
import { getEmDashCollection } from "emdash";
const { entries: posts, error, cacheHint } = await getEmDashCollection("posts", {
orderBy: { published_at: "desc" },
limit: 10,
});
if (error) return new Response("Could not load posts", { status: 500 });
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
---
{posts.map((post) => (
<article>
<h2><a href={`/posts/${post.id}`}>{post.data.title}</a></h2>
</article>
))}
匿名查詢傳回已發布的內容。明確的 status 篩選器適用於已驗證或支援預覽的程式碼。where 接受內容欄位和分類法名稱;完整的篩選器和分頁結構請參閱查詢內容。
查詢單一條目
把 slug 或資料庫 ID 傳給 getEmDashEntry()。下面的路由使用其 URL slug:
---
import { decodeSlug, getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
const slug = decodeSlug(Astro.params.slug);
if (!slug) return Astro.rewrite("/404");
const { entry: post, error, cacheHint } = await getEmDashEntry("posts", slug);
if (error) return new Response("Could not load the post", { status: 500 });
if (!post) return Astro.rewrite("/404");
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
---
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article>
傳回的 entry.id 是 Astro 的路由識別碼,通常就是 slug。資料庫中的內容 ID 是 entry.data.id。對於需要已儲存內容 ID 的輔助函式,請使用 data.id。
使用動態 CMS 功能
EmDash 匯出了一些伺服器端輔助函式,用於不屬於單一集合條目的資料:
---
import { getMenu, getSiteSettings } from "emdash";
import { WidgetArea } from "emdash/ui";
const [menu, settings] = await Promise.all([
getMenu("primary"),
getSiteSettings(),
]);
---
<header>
<a href="/">{settings.title}</a>
<nav>
{menu?.items.map((item) => <a href={item.url}>{item.label}</a>)}
</nav>
</header>
<main><slot /></main>
<aside><WidgetArea name="sidebar" /></aside>
選擇外掛格式
沙箱外掛程式和原生外掛程式的套件結構不同。沙箱外掛程式使用 emdash-plugin.jsonc 加上預設匯出的 src/plugin.ts 物件。原生外掛程式匯出一個描述元工廠,以及使用 definePlugin() 建構的 createPlugin()。
在新增外掛程式之前,請閱讀選擇外掛格式。不要把原生的 definePlugin() 範例複製到沙箱套件中。