Comark内容分发:一份Markdown同时生成博客、邮件与RSS

原创2026-10-15 20:58:141,334 阅读

Comark内容分发:一份Markdown同时生成博客、邮件与RSS

Comark 是一款开源的 Markdown 解析与渲染器,支持组件、属性和插件,可将同一份内容流式输出到 React、Vue、Svelte、Angular、HTML 和终端。它的核心能力是“一次解析、处处渲染”,让一份 Markdown 源稿同时驱动博客、邮件和 RSS 三类渠道,省去重复排版和维护多套模板的成本。

Comark解析与渲染Markdown的开发者主界面

为什么内容分发总是很麻烦 🤯

传统做法里,同一条内容要发三个地方,就得维护三套东西:

  • 博客:一套 Markdown 模板 + 框架渲染组件
  • 邮件:一段内联样式的 HTML 字符串
  • RSS:一段转义后、包着 CDATA 的 XML

每套都有自己的坑:邮件客户端不支持复杂 CSS、RSS 里一个 ]]> 就能毁掉整个 feed、博客里相对链接在别处打不开。内容一改,三处全要同步,新手很容易在这里踩坑。

Comark 的思路:解析一次,到处渲染 🧩

Comark 的解法是把“解析”和“渲染”彻底分开。

parseMarkdown() 把 Markdown 解析成一个可序列化的 JSON 文档(MarkdownDocument),包含 frontmatter(头部数据)、meta(插件元数据,如目录)和 nodes(正文节点)。这个文档不依赖任何框架,存进数据库、放进队列、随 API 传递都行。

Comark内容分发示意图:一份Markdown源稿输出到多种渠道

有了这份“中间文档”,后面的事情就简单了:

渠道 渲染方式 说明
博客页面 @comark/vue、@comark/react 等框架渲染器 浏览器里用组件渲染,支持自定义设计
邮件 @comark/html 的 renderHtml() 输出纯 HTML 字符串,组件用函数映射为内联样式
RSS 同上,再包一层 CDATA 输出合法 XML 片段

文档模型详见 docs/content/1.getting-started/2.document-model.md,核心 API 源码在 packages/comark/src/parse.ts。

渠道一:博客——带 frontmatter 和目录的完整文章 📝

博客场景下,一个 Markdown 文件里可以同时写 YAML 头部、正文和自定义组件:

---
title: Hello World
pubDate: 2026-10-07
---

## Getting started

::alert{type="info"}
用你自己的设计系统渲染组件。
::

服务端或构建时调用一次 parseMarkdown(),就能拿到正文、标题、日期和目录,再交给框架渲染:

const tree = await parseMarkdown(markdown)
tree.frontmatter.title  // 'Hello World'
tree.meta.toc.links     // [{ id: 'getting-started', text: 'Getting started', depth: 2 }]

配合 shiki 插件做代码高亮、toc 插件自动生成侧边栏目录。Nuxt、Next.js、Astro、SvelteKit 都有完整示例,比如 examples/1.frameworks/nuxt/ 里的博客项目,详细指南见 docs/content/10.use-cases/7.docs-and-blogs.md。

Comark博客渲染效果:左侧目录、正文与源码对照

渠道二:邮件——组件直接变成内联样式按钮 ✉️

邮件客户端不运行你的 Vue 或 React 组件,@comark/html 的解法是:把组件标签映射成返回 HTML 字符串的函数,样式直接内联:

const renderEmail = createHtmlRenderer({
  components: {
    button: async ([, attrs, ...children], { render }) =>
      `<a href="${escapeHtml(String(attrs.href))}" style="display:inline-block;padding:12px 20px;background:#0f172a;color:#fff;border-radius:6px">${await render(children)}</a>`,
  },
})

Markdown 里照常写组件语法,就能渲染出邮件按钮:

:button[Confirm your email]{href="https://example.com/confirm"}

还能通过 data 选项给每个收件人传不同的值(比如用户名),实现个性化问候。邮件与 RSS 的完整写法见 docs/content/10.use-cases/10.email-and-rss.md,HTML 渲染器实现位于 packages/comark-html/src/render.ts。

渠道三:RSS——一份合法 XML 的 feed 📡

RSS 的本质是把每条文章的标题和 HTML 包进 CDATA 段落。Comark 的做法是:先用 createHtmlRenderer() 把每篇 Markdown 渲染成 HTML,再写入 <item> 并处理两处易错点:

  1. CDATA 不能包含 ]]>——需要把出现的 ]]> 拆成 ]]]]><![CDATA[>
  2. 链接 URL 要转义 & < >,保证 feed 是合法 XML
<item>
  <title><![CDATA[文章标题]]></title>
  <link>https://example.com/post/1</link>
  <description><![CDATA[文章HTML正文]]></description>
</item>

另外提醒一个细节:邮件和 RSS 阅读器无法解析 /docs 这类相对链接,可以重写 a 标签把 href 补全为绝对地址;如果内容来自用户输入,建议再加上 security 插件做标签过滤。

三条渠道,一份源稿:内容还能继续扩展 🚀

除了博客、邮件、RSS,同一份 MarkdownDocument 还能渲染到更多地方:

  • 网站前台:把 JSON 文档存库,任何客户端用 <MarkdownDocument> 直接渲染,浏览器里无需解析器(参见 docs/content/10.use-cases/8.cms-runtime-content.md)
  • 终端:用 @comark/ansi 输出带样式的 Markdown,适合 CLI 和 AI 编码助手,示例见 examples/3.cli/ansi/render.ts
  • AI 流式输出:auto-close 机制会自动补全未闭合的语法,AI 输出到一半也能稳定渲染

常见问题 💬

Q:邮件 HTML 能兼容所有邮件客户端吗? 不能。Comark 负责把 Markdown 和你的组件函数渲染成 HTML,最终效果取决于你写的标记和 CSS,复杂布局建议仍以内联样式为主。

Q:同一份 Markdown 能同时用在网站和邮件上吗? 可以。网站用框架渲染器,邮件用 @comark/html,为每个目标注册各自的组件即可,源稿完全共用。

Q:已经解析好的文档能直接渲染吗? 可以。renderHtmlFromDocument() 接收已存好的 MarkdownDocument,跳过解析步骤直接出 HTML。

小结

Comark 用“一次解析、到处渲染”的文档模型,把博客、邮件、RSS 三条分发渠道统一到同一份 Markdown 源稿上:框架渲染器管网页,@comark/html 管静态输出。对于需要多渠道路达的内容团队,这套机制能显著减少模板重复和维护成本。更多实战场景可参考 docs/content/10.use-cases/ 下的使用指南。

登录后查看全文
comark