Mermaid 很方便:寫幾行文字,就能變成流程圖。問題是,如果每頁都把 Mermaid library 一起送到瀏覽器,讀者可能只是來看一篇文章,卻先搬一箱 JavaScript 回家。

文章中的流程圖、架構圖通常不需要訪客現場編輯或互動。這類圖適合用 Mermaid CLI 在建置前轉成靜態檔,再用 <img> 顯示——不用每頁載入 Mermaid JavaScript,Vite bundle 也不會因固定圖變胖,還能用 altwidthheight<figcaption> 照顧可讀性與版面穩定。如果圖真的會跟著訪客操作即時變化,才保留瀏覽器端 Mermaid。

SVG、PNG,還是瀏覽器端 Mermaid?

先看圖的用途,不要先看工具名稱。選錯格式,就像拿海報字體去印名片:不是不能用,只是會有點委屈。

情境建議取捨
一般流程圖、架構圖build-time SVG線條與文字縮放清楚;仍要檢查文字大小與對比
固定像素圖片、社群分享、不想處理 SVGbuild-time PNG瀏覽器處理單純;放大可能變糊,檔案也可能較大
圖會因操作即時改變瀏覽器端 Mermaid保留互動,但要負擔下載、解析與執行 JavaScript
想把圖直接放進 HTMLbuild-time inline SVG少一次圖片請求;HTML 變大,也要審查 SVG 內容

多數教學文章先選 SVG。若你的分享流程、圖片處理或內容系統更適合 PNG,也可以在同一次 build 產出 PNG。重點不是「PNG 永遠比較快」,而是不要讓每個頁面都執行整套繪圖 library。

若 Vite 出現 chunk 過大警告,先確認是不是 Mermaid 造成的。build.chunkSizeWarningLimit 預設 500 kB,只是警告門檻,調高只會讓終端機安靜,不會讓下載變快。固定圖改走 build-time 匯出;只有少數頁面需要互動圖時,再用 import('mermaid') 延遲載入並限制在需要的頁面。最後用 Network 和 Lighthouse 確認:不看圖的頁面,不應再載入 Mermaid chunk。

第一步:準備 Mermaid 原始檔

例如建立 src/diagrams/publish-flow.mmd

flowchart TD
  A[寫文章] --> B{檢查}
  B -->|通過| C[Eleventy build]
  B -->|未通過| A
  C --> D[輸出靜態網站]

.mmd 是圖表的原始碼。它像食譜;SVG 或 PNG 才是端上桌的蛋糕。讀者通常只需要蛋糕,不需要一起下載烤箱。

第二步:用 Mermaid CLI 匯出 SVG 或 PNG

Mermaid 官方 CLI 的套件名稱是 @mermaid-js/mermaid-cli,命令名稱是 mmdc。可以全域安裝:

npm install -g @mermaid-js/mermaid-cli
mmdc -i "src/diagrams/publish-flow.mmd" -o "src/assets/diagrams/publish-flow.svg"
mmdc -i "src/diagrams/publish-flow.mmd" -o "src/assets/diagrams/publish-flow.png"

或者裝在專案內,讓本機和 CI 使用同一套依賴:

npm install --save-dev @mermaid-js/mermaid-cli
./node_modules/.bin/mmdc \
  -i "src/diagrams/publish-flow.mmd" \
  -o "src/assets/diagrams/publish-flow.svg"

mmdc 會依輸出副檔名產生 SVG、PNG 或 PDF。也可以在輸出時指定主題與透明背景:

mmdc -i input.mmd -o output.png -t dark -b transparent

網站有深色模式時要多留意:SVG 能縮放,不代表它會自動換成深色配色。需要時在 build 時產生 light/dark 兩份,或固定一套在兩種背景都看得清楚的主題。

第三步:讓 Eleventy 複製圖檔並嵌入頁面

如果圖片放在 src/assets/diagrams/,在 eleventy.config.js 加入 passthrough copy:

export default function (eleventyConfig) {
  eleventyConfig.addPassthroughCopy({
    "src/assets/diagrams": "assets/diagrams"
  });
}

這會把來源目錄複製到輸出目錄的 assets/diagrams。接著,文章或模板可以用普通 HTML 顯示圖片:

<figure class="diagram">
  <img
    src="/assets/diagrams/publish-flow.svg"
    alt="發布流程:寫文章後先檢查;通過才執行 Eleventy build,失敗則回到修改文章。"
    width="900"
    height="520"
    loading="lazy"
    decoding="async">
  <figcaption>圖一:文章從檢查到 Eleventy build 的流程。</figcaption>
</figure>

如果 Markdown 設定允許原始 HTML,直接放上面的 <figure> 即可。這裡要放的是 <img src="...">,不是 <div class="mermaid">。後者只是標記,仍需要 Mermaid JavaScript 在瀏覽器把它畫出來。

若 Markdown 會過濾 HTML,可改用 Nunjucks/WebC layout 或 shortcode 輸出同一段 HTML。不要為了繞過過濾器,就把所有內容無條件標成 safe

alt 應描述讀者從圖中需要知道的結論或流程,不要只寫「流程圖」。如果正文已完整說明同一個流程,圖片可以用 alt="",避免螢幕閱讀器重複朗讀——但正文不能因此消失。不要只靠顏色區分成功與失敗;把差異寫在節點文字、箭頭標籤或正文裡。

第四步:讓圖片在手機上不拆台

圖片 CSS 可以先從這個版本開始:

.diagram {
  max-width: 100%;
  margin: 2rem 0;
}

.diagram img {
  display: block;
  width: 100%;
  height: auto;
  max-width: 100%;
}

.diagram figcaption {
  margin-top: 0.5rem;
  color: var(--muted, #666);
}

widthheight 要填輸出圖片的實際像素尺寸。瀏覽器可以先依比例保留空間,降低圖片載入時內容跳動;如果比例填錯,反而可能失真或預留錯誤空間。

寬流程圖在手機上常見兩種結果:

  • 圖縮到看不清楚:改成較短的縱向流程。
  • 圖不能再縮:提供原尺寸圖片連結,讓讀者自行放大查看。

如果有不同寬度或解析度版本,再考慮 <picture>srcset。不要把一張超大 PNG 無條件送給所有裝置。

上線前檢查清單

  • [ ] .mmd 與輸出的圖片都有納入版本控制,或 build 能穩定重建。
  • [ ] mmdc 固定在專案 dev dependency,避免 CI 和本機產出不同。
  • [ ] 輸出路徑與 <img src> 在開發伺服器、正式站與子路徑部署都正確。
  • [ ] Light/dark mode 下文字與背景都有足夠對比。
  • [ ] build 後確認不看圖的頁面沒有載入 Mermaid chunk。

結語

固定圖先匯出,互動圖才在需要時載入 Mermaid。下一次新增 Mermaid 圖時,用 mmdc 產出 SVG 或 PNG,放進 Eleventy 的 passthrough asset,再用帶有 alt、尺寸和圖說的 <figure> 顯示。先改一張圖,再看 build 與 Network 數據。

參考資料