Markdown 渲染管線設計:從 MDX 到 HTML 的完整鏈路

技术架构(更新於 2026年5月29日)

Markdown 渲染的挑戰

工具庫的部落格與教學系統需要將 MDX/Markdown 檔案渲染為安全的 HTML。看似簡單,實際涉及:

  • 語法擴充:GFM 表格、任務清單、刪除線
  • 安全消毒:防止 XSS(使用者不可信內容)
  • 程式碼高亮:語法著色的 <pre><code> 區塊
  • 標題錨點:自動產生 slug 供目錄跳轉
  • 內部連結/pdf/merge 在建置時轉為帶 locale 前綴的連結

unified 生態管線

MDX/Markdown 原文
     ↓ remark-parse        (Markdown → MDAST)
     ↓ remark-gfm          (GFM 擴充)
     ↓ remark-rehype        (MDAST → HAST)
     ↓ rehype-slug          (標題添加 id)
     ↓ rehype-stringify     (HAST → HTML)
HTML 輸出

工具庫在 src/lib/blog/render-mdx.ts 中實作這條管線:

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkGfm from 'remark-gfm';
import remarkRehype from 'remark-rehype';
import rehypeSlug from 'rehype-slug';
import rehypeStringify from 'rehype-stringify';

const processor = unified()
  .use(remarkParse)
  .use(remarkGfm)
  .use(remarkRehype, { allowDangerousHtml: false })
  .use(rehypeSlug)
  .use(rehypeStringify);

Frontmatter 解析

每篇文章的元資料透過 gray-matter 從 MDX 開頭擷取:

---
title: "文章標題"
description: "SEO 描述"
author: "老張(工具庫站長)"
---

正文內容...
import matter from 'gray-matter';

const { data: frontmatter, content } = matter(rawMdx);
// frontmatter.title → "文章標題"
// content → 正文 Markdown

Frontmatter 用於 SEO metadata、Open Graph 標籤與 JSON-LD 結構化資料。


安全考量

禁止原始 HTML 注入

.use(remarkRehype, { allowDangerousHtml: false })

設為 false 時,Markdown 中的 <script> 標籤會被跳脫為文字,而非執行。

連結安全

外部連結應添加 rel="noopener noreferrer"target="_blank"

// rehype 外掛:為外部連結添加安全屬性
function rehypeExternalLinks() {
  return (tree) => {
    visit(tree, 'element', (node) => {
      if (node.tagName === 'a' && isExternal(node.properties.href)) {
        node.properties.target = '_blank';
        node.properties.rel = 'noopener noreferrer';
      }
    });
  };
}

多語言內容管理

工具庫的內容檔案組織:

src/content/blog/my-article/
  ├── meta.json          ← 共用元資料(分類、標籤、日期)
  ├── zh-CN.mdx          ← 簡體中文正文
  ├── zh-TW.mdx          ← 繁體中文正文
  ├── en.mdx             ← 英文正文
  └── ja.mdx             ← 日文正文

meta.json 儲存與語言無關的資料,*.mdx 儲存各語言的正文與 frontmatter。建置時 generateStaticParams 列舉所有 slug × locale 組合以預渲染。


效能優化

建置時渲染 vs 執行時渲染

工具庫選擇建置時渲染(SSG):

next build → 讀取 MDX → unified 處理 → HTML 寫入 out/
執行時 → 直接回傳靜態 HTML(零處理開銷)

相較每次請求都處理 Markdown,SSG 的 TTFB 從 ~200ms 降至 ~50ms。

增量內容更新

新增或修改文章後需要重新 next build。對於 18+ 篇文章 × 4 語言 = 72 個頁面,完整建置約 3–5 分鐘。


與 MDX 元件的關係

目前工具庫使用純 Markdown(非 React 元件 MDX),優點是:

  • 渲染管線簡單,無 React SSR 開銷
  • 內容檔案可用任何 Markdown 編輯器編輯
  • 翻譯工作只需處理純文字

未來若需在文章中嵌入互動元件(如 live demo),可升級至 @next/mdx + React 元件。


總結

Markdown 渲染管線是內容驅動網站的核心基礎設施。remark/rehype 生態提供可組合的處理器鏈,gray-matter 處理元資料,建置時渲染確保極致效能。工具庫的部落格系統正是這套架構的實戰應用。

本站提供瀏覽器本地工具,免註冊即可試用 →

#Markdown#MDX#remark#rehype#内容渲染