EmDash Starter-Cloudflare 模板指南:基于 Astro 与 Cloudflare Workers 的最小化 CMS 起步方案
2026/9/24 20:52:53 网站建设 项目流程
  • CMS
  • 后端
  • 前端
  • 插件系统

【免费下载链接】emdash

EmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress

项目地址:https://gitcode.com/gh_mirrors/emdas/emdash
点击查看免费下载

导读

templates/starter-cloudflare是 EmDash 官方提供的通用起步模板,它把一个带完整管理后台的 Astro CMS 站点直接部署到 Cloudflare Workers 之上:使用 D1 作为数据库、R2 作为媒体存储,站点默认包含 posts / pages 两个内容集合以及 category / tag 两个分类法。读完本文,你将掌握该模板的文件结构、核心命令、schema 定义方式、服务端渲染(SSR)规范,并能在其基础上快速搭建属于你自己的内容站点或将其替换为 blog / portfolio / marketing 等带完整视觉系统的模板。

模板定位:最小化、不预设视觉风格

该模板的定位是一个「通用起点」:包含 posts、pages、categories、tags,但没有预置theme.css、没有自定义字体配置、也没有超越浏览器默认样式的排版。主页、文章列表、文章详情、页面、分类页、标签页全部以极简样式渲染(templates/starter-cloudflare/CLAUDE.md)。

这带来的结果是:该模板的定制工作是「做加法」而不是「做减法」——自带内容、schema、路由与后台,只缺设计层。如果你想要开箱即用的设计模板,应改用blogportfoliomarketing(它们都带可通过theme.css换肤的完整视觉系统)。

常用命令与后台入口

模板在 package.json 中预置了五组脚本:

pnpm dev # 启动 Astro 开发服务器(等价于 astro dev) pnpm build # 构建生产版本(astro build) pnpm preview # 本地预览构建产物(astro preview) pnpm deploy # 构建后通过 wrangler deploy 发布到 Cloudflare Workers pnpm typecheck # 用 astro check 做类型检查

针对 EmDash 特有的两个高频命令:

pnpm dev # 启动 Astro 开发服务器 npx emdash types # 基于运行中的站点重新生成 TypeScript 类型

管理后台地址固定为http://localhost:4321/_emdash/admin(templates/starter-cloudflare/CLAUDE.md)。emdash-env.d.ts中的集合类型会在开发服务器启动时自动重新生成,npx emdash types用于手动触发同一过程。

关键文件速览

文件用途
astro.config.mjsAstro 配置,注册emdash()集成、数据库与存储绑定
src/live.config.tsEmDash loader 注册(样板代码,不要修改)
seed/seed.jsonSchema 定义 + 演示内容(集合、字段、分类法、菜单、widgets)
emdash-env.d.ts集合的生成类型(开发服务器启动时自动重新生成)
src/layouts/Base.astro基础布局,包含 EmDash 接线(菜单、搜索、页面贡献)
src/pages/Astro 页面,全部服务端渲染

配置层:D1 数据库 + R2 媒体存储

astro.config.mjs 是理解模板运行环境的关键。它做了四件事:

  1. 声明output: "server"——这是 EmDash 内容站点必须的 SSR 模式;
  2. 挂载@astrojs/cloudflare适配器;
  3. 通过@emdash-cms/cloudflare包提供d1()r2()两个适配函数,分别绑定 D1 数据库(binding 为DB)与 R2 媒体桶(binding 为MEDIA),session: "auto"表示 D1 会话由集成自动管理;
  4. 注册emdash()集成,并关闭devToolbar

对应的 wrangler.jsonc 声明了 Worker 运行时环境:compatibility_datenodejs_compat兼容标志、名为my-emdash-site的 D1 数据库、名为my-emdash-media的 R2 桶,以及一个每分钟触发的crons维护定时器("* * * * *")。文件里还注释说明了:沙箱化插件需要 Worker Loader(Workers 付费计划),如需启用可取消worker_loaders绑定的注释。

Worker 入口 src/worker.ts 从@emdash-cms/cloudflare/worker引入默认 handler、createScheduledHandlerPluginBridge,并导出scheduled定时处理——这是模板能运行维护型定时任务的底层接线。

数据层:live.config.ts 与 loader 注册

src/live.config.ts 全文只有 6 行:通过astro:contentdefineLiveCollection配合emdash/runtimeemdashLoader()注册一个名为_emdash的 live collection。它把 EmDash 的内容查询能力接入 Astro Content Layer,是页面中getEmDashCollection/getEmDashEntry等 API 能够工作的前提,因此模板明确标注「boilerplate — don't modify」。

Schema 详解:seed.json

模板的数据模型全部定义在 seed/seed.json 中,它同时承担 schema 定义与演示内容两种职责,package.json里的"emdash": { "seed": "seed/seed.json" }字段将其与 CLI 工具关联。

集合与字段

  • poststitle(string,必填、可搜索)、featured_image(image)、content(portableText,可搜索)、excerpt(text),支持 drafts / revisions / search / seo;
  • pagestitle(string,必填、可搜索)、content(portableText,可搜索),带urlPattern: "/{slug}",支持 drafts / revisions / search。

分类法与菜单

  • 分类法category(层级化hierarchical: true,预置generalupdates两个 term)与tag(非层级化,预置starterexample)都只挂载在posts集合上;
  • 单一primary菜单,包含 Home、Posts、About 三个自定义链接;
  • widgetAreas定义了一个名为sidebar的 widget 区域,预置了三个组件 widget:core:search搜索、core:categories分类列表、core:recent-posts最近文章(count: 5)。

演示内容

seed 内还包含一页(/about)与一篇文章(/posts/welcome)。文章的content以 Portable Text 块结构书写(_type: "block"+_type: "span"的 children),并演示了taxonomies字段如何关联categorytag的 term slug。

站点设置(settings)只有title(My Site)与tagline(Built with EmDash)两个字段,与src/utils/site-identity.ts中的resolveStarterSiteIdentity工具函数对应——该函数把设置解析为siteTitle/siteTagline/siteLogo,并在无设置时回退到"My Site"/"Built with EmDash"默认值。

页面路由与服务端渲染

模板的src/pages/下所有页面均为 SSR(output: "server"),不适用getStaticPaths()。路由清单(templates/starter-cloudflare/CLAUDE.md):

页面路径展示内容
Home/站点标题 + tagline,链接到 Posts / About
All posts/posts文章列表
Post detail/posts/[slug]文章内容
Page/[slug]静态页面内容(如/about
Category/category/[slug]按分类过滤的文章
Tag/tag/[slug]按标签过滤的文章

主页与列表页的查询模式

src/pages/index.astro 演示了最基础的查询:getEmDashCollection("posts", { orderBy: { published_at: "desc" } })按发布时间倒序取文章,并通过Astro.cache.set(cacheHint)写入缓存提示(Cloudflare 适配器会把它转换为页面缓存)。首页还会在无文章时输出一个指向/emdash/admin/content/posts/new的「创建文章」链接。

文章列表页 templates/starter-cloudflare/src/pages/posts/index.astro 则演示了批量查询 taxonomy 的优化做法:先用getEmDashCollection取全部文章,再用getTermsForEntries("posts", posts.map(p => p.data.id), "tag")一次性批量获取所有文章的标签映射,避免对每篇文章单独调用getEntryTerms造成 N 次往返。

详情页、分类页与标签页

文章详情页 templates/starter-cloudflare/src/pages/posts/[slug].astro 是内容型页面的完整示例:

  • decodeSlug(Astro.params.slug)解码 slug,非法或未命中时Astro.redirect("/404")
  • getEmDashEntry("posts", slug)取单条 entry,并通过getSeoMeta基于内容字段生成 SEO meta(title / description / canonical / ogImage),传入Base布局;
  • post.data.terms?.tag ?? []post.data.terms?.category ?? []读取由getEmDashEntry预先水合(hydrate)好的分类法 term;
  • emdash/uiPortableText渲染 Portable Text 正文,用Image渲染图片字段;
  • 关键细节:编辑高亮依赖{...post.edit.featured_image}{...post.edit.title}属性展开,这是 EmDash 可视化编辑能力的接线点。

通用页面 templates/starter-cloudflare/src/pages/[slug].astro 结构相同,仅渲染titlePortableText正文。分类页与标签页(category/[slug].astro、tag/[slug].astro)模式一致:先getTerm("category" | "tag", slug)取 term,再用getEmDashCollection("posts", { where: { category: term.slug } }){ where: { tag: term.slug } }过滤文章。

基础布局 Base.astro

src/layouts/Base.astro 承担所有页面的外壳:

  • 通过getMenu("primary")取菜单、getSiteSettings()取站点身份并渲染导航栏,导航内嵌emdash/ui/searchLiveSearch(搜索集合限定为postspages);
  • 通过createPublicPageContext创建公共页面上下文(区分 content / custom 两种页面类型),并渲染EmDashHeadEmDashBodyStartEmDashBodyEnd三兄弟组件完成 SEO、脚本与插件页面贡献(page contributions)的注入;
  • <footer>里通过<WidgetArea name="sidebar" />渲染 sidebar widget 区域。

必须遵守的编码规则

CLAUDE.md 明确列出了在模板中开发时必须遵守的五条硬性规则(templates/starter-cloudflare/CLAUDE.md),它们直接决定代码能否在 Cloudflare 环境下正确工作:

  1. 所有内容页面必须服务端渲染output: "server"),禁止对 CMS 内容使用getStaticPaths()
  2. 图片字段是对象{ src, alt }而非字符串,必须用emdash/ui导出的<Image image={...} />渲染——这在所有页面示例中均有体现;
  3. entry.id是 slug(用于 URL),而entry.data.id是数据库 ULID(用于getEntryTerms等 API 调用)——例如列表页批量取标签时传的是post.data.id,而拼链接时用的是post.id
  4. 查询内容的页面必须调用Astro.cache.set(cacheHint)
  5. 查询中的 taxonomy 名称必须与 seed 的"name"字段完全一致(如"category"而非"categories")。

视觉定制:从零开始建立设计系统

模板刻意不施加任何视觉风格(「None imposed. Define your own.」),它默认不包含(templates/starter-cloudflare/CLAUDE.md):

  • src/styles/theme.css——需要 CSS 变量主题时创建它并从Base.astro引入;
  • astro.config.mjs中的字体——fonts: []为空数组,需要 web 字体时按cssVariable绑定方式添加 Google Fonts 条目;
  • 带样式的components/目录(卡片、标签列表等)——按需自行构建。

合理的首轮定制步骤(templates/starter-cloudflare/CLAUDE.md):

  1. 选择一套展示字体 + 一套正文字体,写入astro.config.mjs并绑定到--font-display--font-bodyCSS 变量;
  2. 创建src/styles/theme.css,定义配色、字号阶梯与间距 token;
  3. Base.astro中引入——布局已经自带一个小型 reset,把主题样式放在页面样式之上;
  4. 在各个 Astro 页面的<style>块中引用这些 CSS 变量构建页面级样式。

同时有三条「不要做」的边界(templates/starter-cloudflare/CLAUDE.md):

  • 不要把它当作成品设计——无样式输出是有意为之,直接上线会显得未完成;
  • 不要不加考虑地引入组件库(Tailwind UI、shadcn 等)——模板「小」是刻意设计;
  • 不要在这里复刻 blog 模板的三栏阅读视图——想要那种效果应直接从blog模板开始。

Agent 技能与文档支持

模板面向 AI 编码助手做了完整铺垫(templates/starter-cloudflare/CLAUDE.md)。仓库根目录下对应三份技能文档:

  • building-emdash-site(skills/building-emdash-site/SKILL.md)——内容查询、Portable Text 渲染、schema 设计、seed 文件、站点特性(菜单、widgets、搜索、SEO、评论、byline),建议从这里开始;
  • creating-plugins(skills/creating-plugins/SKILL.md)——用 hooks、storage、管理后台 UI、API 路由与 Portable Text 块类型构建 EmDash 插件;
  • emdash-cli(skills/emdash-cli/SKILL.md)——内容管理、seeding、类型生成与可视化编辑流程相关的 CLI 命令。

EmDash 官方文档还以 MCP 服务器形式提供(https://docs.emdashcms.com/mcp),当需要核对某个 API、hook、配置项、字段类型或模式时,应通过search_docs查询实时文档而不是依赖训练数据的记忆。模板随附.mcp.json.cursor/mcp.json.vscode/mcp.json,Claude Code、Cursor、VS Code 会自动发现文档服务器;其他工具(OpenCode、Windsurf 等)需要一次性手动配置。

总结:从模板到站点的路径

starter-cloudflare的价值在于它把「能跑起来的 EmDash 站点」压缩到最小:SSR 渲染、D1 + R2 存储、完整的 posts/pages/category/tag 内容模型、后台管理界面与搜索都已就绪,剩下的工作全部集中在视觉层。你可以沿用它的查询与渲染模式,参考 templates/starter-cloudflare/src/pages 下每个页面的写法,配合 templates/starter-cloudflare/seed/seed.json 扩展自己的集合与字段;也可以直接改用带完整视觉系统的blogportfoliomarketing模板,再通过theme.css换肤。

  • CMS
  • 后端
  • 前端
  • 插件系统

【免费下载链接】emdash

EmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress

项目地址:https://gitcode.com/gh_mirrors/emdas/emdash
点击查看免费下载
上一篇:解锁OrcaSlicer:从源码编译到定制化开发的实战指南
下一篇:2025年扩散模型架构演进:从U-Net基础到多模态融合

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询