面向 Astro 開發者的 EmDash

本頁內容

EmDash 為 Astro 站台新增了管理應用程式、由資料庫支援的集合、媒體、選單、分類法、設定、修訂和預覽。頁面和元件仍然是一般的 Astro 檔案。

EmDash 新增了什麼

功能提供的能力
管理後台在 /_emdash/admin 透過瀏覽器管理集合、媒體、選單、分類法和設定
資料庫集合在請求時查詢的、由編輯者管理的內容
媒體庫已儲存的圖片和檔案,並為範本提供媒體欄位值
草稿、修訂和預覽發布之前的編輯工作
選單和小工具區域位於條目欄位之外的、有序且可編輯的站台區域
站台設定共用的識別和顯示值,例如標題、標語、標誌和分頁大小
外掛程式勾點、路由、儲存和選用的管理後台擴充

這些功能與 Astro 並存,而不是取代它。路由、版面配置、算繪、樣式和部署介接器仍然由 Astro 控制。

EmDash 和 Astro 集合

Astro 內容集合和 EmDash 集合可以共存。儲存庫所擁有的內容使用 Astro 集合,透過 /_emdash/admin 管理的內容使用 EmDash。

Astro 內容集合EmDash 集合
儲存專案中的檔案SQL 資料庫
編輯儲存庫工作流程EmDash 管理後台
查詢getCollection()getEmDashCollection()
富文字Markdown 或 MDXPortable 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() 範例複製到沙箱套件中。

下一步