目前使用 Iansui 襯體

Writing

Astro 與 EmDash

初次使用 Astro.js

2026 年初 Cloudflare 正式收購 Astro 的核心團隊後,於同年 4 月推出了完全以 TypeScript 打造的開源內容管理系統(CMS)——EmDash。官方將其定位為「WordPress 的現代精神繼承者」,核心目標是解決長年困擾傳統 CMS 的外掛安全性問題,並結合 Astro 的渲染能力與 Cloudflare 的邊緣(Edge)運算架構,打造一個原生支援 TypeScript、具備沙箱安全隔離機制的無伺服器 CMS。

EmDash 核心架構與技術特點

EmDash 並非單純在 Astro 上套一層後台外皮,而是一套重構傳統 CMS 架構的全端解決方案:

  • 邊緣無伺服器架構:預設運行於 Cloudflare Workers 之上,持久化儲存採用 Cloudflare D1(分散式關聯式 SQLite 資料庫),媒體靜態資源則直接對接 Cloudflare R2。同時,它也支援在標準 Node.js 環境搭配本機 SQLite 執行。
  • 沙箱化外掛機制(Sandboxed Plugins):傳統 WordPress 最大的安全弱點在於外掛以全域權限直接執行於主伺服器行程中。EmDash 採用 V8 Isolates 與基於能力(Capability-based)的安全模型,外掛只能存取受明確授權的 API,無法隨意讀寫全域記憶體或任意操作資料庫。
  • 結構化內容(Portable Text):放棄傳統 CMS 儲存雜亂 HTML 標記的做法,內容資料皆以結構化 JSON 物件形式儲存,方便直接在 Astro 模板中解構與安全渲染。
  • 端到端型別安全:從資料庫 Schema、內容定義到前端元件,全部由 TypeScript 貫穿,提供完整的靜態型別檢查與代碼補全。

為什麼要用 vs. 為什麼不要用

在技術選型上,EmDash 具備明顯的現代架構優勢,但也有新興專案不可避免的妥協:

評估維度

選擇 EmDash(Why to use)

暫緩使用 EmDash(Why NOT to use)

安全性

外掛預設在沙箱環境執行,大幅杜絕外掛供應鏈漏洞與 SQL 注入風險。

平台本身剛進入穩定階段,尚未經歷長年的大規模實戰攻擊驗證。

營運成本

藉由 Cloudflare Workers「縮放至零」(Scale to zero)的特性,低流量站點幾乎零成本,高流量站點自帶全域 CDN 快取。

依賴邊緣環境的運算額度,若有長時間背景批次處理,邊緣執行時間限制會成為阻礙。

開發體驗

現代全端 TypeScript 堆疊,不需要維護 PHP、Apache 或肥大的 MySQL VM。

團隊若缺乏現代前端與 TypeScript 基礎,維護與除錯門檻遠高於傳統虛擬主機。

生態豐富度

架構乾淨,開箱即支援 Astro 生態的所有元件庫(Tailwind、React、Svelte 等)。

社群外掛、主題市場與現成解決方案數量極少,無法像 WordPress 般開箱即用多數功能。

對 Astro 使用者有什麼實質幫助?

對於本就熟悉 Astro 的工程師而言,EmDash 解決了靜態網站生成器(SSG)與內容驅動站點長期的痛點:

  1. 取代臃腫或昂貴的 Headless CMS:以往 Astro 處理非工程師內容編輯需求時,往往需要串接外部 SaaS(如 Contentful、Sanity)或自建 Strapi。EmDash 讓你在同一個專案架構內同時擁有後台管理介面與 Astro 前端,省去額外的認證機制與 API 網路往返延遲。
  2. 免除純 Git 工作流的協作阻礙:Astro 內建的 Content Collections 依賴工程師提交 Markdown 或 MDX。EmDash 提供給行銷或內容營運人員直覺的視覺化編輯後台,但底層產出的內容依然能無縫對接到 Astro 的內容管線中。
  3. 極致的原生邊緣效能:由於同屬 Cloudflare 生態系,Astro 的 Islands 架構(獨立島嶼架構)與 Cloudflare 邊緣中繼資料庫 D1 能達成低延遲的伺服器端渲染(SSR),不再需要複雜的快取失效機制。

EmDash 快速上手指南

如何在本地建立並啟動一個基於 EmDash 的 Astro 專案:

1. 初始化專案

透過官方 CLI 建立專案骨架:

npm create emdash@latest my-blog
cd my-blog
npm install

2. 配置資料庫與環境

EmDash 預設支援以本機 SQLite 進行開發,或透過 Wrangler 連線至 Cloudflare D1。檢視專案根目錄下的 wrangler.jsonc(或 wrangler.toml),確認 D1 綁定設定:

{
  "name": "my-blog",
  "compatibility_date": "2026-04-01",
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "emdash-prod",
      "database_id": "<YOUR_D1_DATABASE_ID>"
    }
  ]
}

執行本機資料庫遷移(Migration)建立基礎資料結構:

npx wrangler d1 migrations apply DB --local

3. 在 Astro 中定義內容模式(Schema)

EmDash 採用類似 Astro Content Layer 的宣告方式。在 emdash.config.ts 中宣告你的內容型別:

import { defineCollection, defineField } from 'emdash';

export const collections = {
  posts: defineCollection({
    name: '文章',
    fields: [
      defineField({ name: 'title', type: 'string', required: true }),
      defineField({ name: 'slug', type: 'slug', from: 'title' }),
      defineField({ name: 'content', type: 'portableText', required: true }),
      defineField({ name: 'publishedAt', type: 'date' }),
    ],
  }),
};

4. 啟動本機開發伺服器

npm run dev

啟動後,終端機將提供兩個端點:

進入後台後,即可建立初始管理者帳號,並透過視覺化介面新增與發布文章。

5. 在 Astro 頁面中渲染內容

src/pages/blog/[slug].astro 中直接提取 EmDash 資料庫內容:

---
import { getEmDashClient } from 'emdash/runtime';
import { PortableText } from 'emdash/components';

const { slug } = Astro.params;
const emdash = getEmDashClient(Astro.locals);

const post = await emdash.collections.posts.findOne({ where: { slug } });
if (!post) return Astro.redirect('/404');
---

<article class="max-w-3xl mx-auto py-8">
  <h1 class="text-4xl font-bold mb-4">{post.title}</h1>
  <time class="text-gray-500">{post.publishedAt}</time>
  <div class="prose mt-6">
    <PortableText value={post.content} />
  </div>
</article>

6. 部署至 Cloudflare

完成本機測試後,將資料庫遷移套用至雲端 D1,並直接部署:

# 套用遠端資料庫結構
npx wrangler d1 migrations apply DB --remote

# 部署 Astro 與 EmDash 邊緣端點
npm run build
npx wrangler deploy

結語

EmDash 代表了現代內容管理系統的一種新嘗試:將內容結構化與邊緣計算的效能優勢結合,同時用型別安全與沙箱隔離取代傳統腳本語言直接操作底層伺服器的安全隱患。對於已經採用 Astro 並渴望脫離第三方 SaaS CMS 束縛的工程師團隊來說,這是一套架構乾淨、維運負擔極低,且高度契合邊緣架構的解決方案。