IanChen

Next.js 部落格架構:實現「一文一目錄」的自動化流程

透過腳本將 Markdown 相對路徑自動轉為 Next.js 格式,解決靜態站圖片管理痛點

avatar奕安Feb 9, 2026

我在 2023 年對自己的部落格進行了一次重寫。當時對前端架構有了新的理解,也希望能完全掌控程式碼,於是選擇用 Next.js 製作純靜態匯出 (Static Export),部署在 Vercel 上,沒有後端資料庫。

網站的外觀與風格全由自己定義,目標是簡練且良好的閱讀體驗。而在架構上,這個站比較特別的地方在於內容產生的 Pipeline:我並非直接編輯 Next.js 的原始檔案,而是採用「一篇文章一個資料夾」搭配 Markdown 相對路徑寫作,最後透過腳本自動轉譯成 Next.js 所需的格式與圖片路徑。

TL;DR

平時寫文章時,我不直接改動 Next.js 讀取的檔案,而是寫在 content-src 目錄下,採用一文一目錄的結構,並直接使用相對路徑引用圖片。

發布前,我會執行一支 Python 腳本,將內容轉換成 content-generated 的扁平化 Markdown 列表,並將圖片自動搬移至 public/post-images,最後才交給 Next.js 進行靜態生成 (SSG)。

整體架構概觀

對使用者來說,這就是一個標準的 Next.js 靜態網站:首頁是文章列表(含分頁),點進去是單篇文章,另有 About、Archive 頁面與深色模式。網站部署在 Vercel,網址是 ianchen.tw

但與一般靜態站不同的是,我將**「寫作環境」「部署環境」**拆開了:

  1. 寫作端 (Source):每篇文章獨立一個資料夾。Markdown 檔與圖片放在一起,使用相對路徑引用(如 ./foo.jpg),完全不必理會網站最終的 URL 結構。
  2. 轉換層 (Transform):透過 Python 腳本,將每篇文章的圖片搬移到網站 public 資料夾,並修改 Markdown 內的圖片連結為絕對路徑,產出給 SSG 使用的檔案。
  3. 建置端 (Build):Next.js 只讀取轉換後的產物。利用 Front Matter 抓取標題與日期,內文則透過 MDX 搭配 GFM (GitHub Flavored Markdown) 與程式碼高亮渲染。

嚴格來說,這個站是「靜態站 + 一套離線的內容 Pipeline」,Git Repo 裡的原始 Markdown 並非直接的 Source of Truth,轉換後的才是。

技術實作細節

1. 為什麼要分開「寫作格式」和「發布格式」?

我的核心需求是:盡可能好管理文章與圖片

在 Next.js 的標準實作中,圖片通常得放在 public 資料夾才能被存取。這導致寫作 Markdown 時,圖片與文章內容是分離的,預覽也不直觀。

為了在「好寫的 Markdown + 相對路徑」和「Next.js 要求的 public 結構」之間取得平衡,我制定了一套命名規則,並讓程式去處理繁瑣的路徑問題。

寫作時維持「一篇文章一個資料夾」:同一篇的圖片和 Markdown 放在同一個目錄,引用時直接寫相對路徑。這樣做的心智負擔最小,也不容易寫錯路徑。至於網站到底要用什麼 URL,交給腳本處理就好。

2. 文章與圖片的轉換流程

實作上,腳本會掃描 content-src 下的所有目錄,並執行以下兩個步驟:

  1. 搬運圖片:將該資料夾內的圖片,複製到網站的 public 結構下(例如 public/post-images/<slug>/)。
  2. 路徑重寫 (Rewrite):讀取原始 Markdown,將內文與 Front Matter 中的相對路徑(含封面圖),改寫為對外可用的絕對路徑,最後輸出一份「給 SSG 吃的」單一 Markdown 檔。

這樣一來,SSG 拿到的就是已經處理好的正確路徑,不需要在 Runtime 或 Build time 猜測圖片位置。實際的 Pipeline 轉換如下:

寫作端 (content-src/)                    轉換後
────────────────────────────────────────────────────
2024-06-01-sunday-nap/                 content-generated/
  index.md                               2024-06-01-sunday-nap.md
  cat-on-sofa.jpg
  coffee.png                           public/post-images/
  cover.jpg                              2024-06-01-sunday-nap/
                                           cat-on-sofa.jpg
                                           coffee.png
                                           cover.jpg
 

左圖:寫作時,Markdown 與圖片共存在同一目錄,使用相對路徑。 右圖:轉換後,產出扁平化的 Markdown 檔(檔名即 slug),圖片則被整理至 public 對應目錄。

重點在於 Pipeline 會改寫 Markdown 內的連結。例如寫作時寫 ./cat-on-sofa.jpg,轉換後的 Markdown 會自動變成 /post-images/2024-06-01-sunday-nap/cat-on-sofa.jpg。Next.js 讀取時,內容與實際檔案結構已經完全一致。

3. 靜態站 (Next.js) 做什麼?

由於複雜的路徑邏輯都在轉換層處理完了,Next.js 的工作就變得很單純:

  • Build:掃描 content-generated 資料夾。
  • Data Fetching:讀取 Front Matter 獲得標題、日期、摘要與封面圖,並依日期排序生成列表。
  • Rendering:內文使用標準 Markdown 渲染,程式碼區塊套用 Syntax Highlighting。
  • UI:深色模式、分頁與導覽列皆使用 Tailwind CSS 手刻,未套用現成主題。

我怎麼管理文章

在這個架構下,我的日常寫作流程非常單純:

  1. 開新文章:新增一個資料夾,命名習慣是「日期 + slug」,裡面放一個 index.md
  2. 撰寫內容:填寫 Front Matter,內文照常寫。圖片直接丟進該資料夾,用相對路徑引用。
  3. 發布:執行轉換腳本並 Build,確認沒問題後即可 Deploy。

這樣做最大的好處是:寫作時你只需要關心「這篇文章的資料夾」。你不需要記憶網站上的 URL 結構,也不用手動修改轉換後的檔案。未來如果想更換 SSG 框架或改變網址規則,只需要修改轉換腳本,寫作端的原始檔案完全不需要動。

小結

對外,這是一個普通的 Next.js 靜態站;對內,這是一個擁有自動化 Pipeline 的內容系統。

透過**「用自己習慣的目錄結構寫作」搭配「CLI 轉換成靜態站格式」**,我解決了圖片路徑管理的痛點。雖然這比直接把 Markdown 丟進 Repo 多了一層手續,但換來的是寫作時的專注與輕鬆。

如果你也在思考如何長期維護個人部落格,不妨試試將「寫作格式」與「發布格式」分開處理。當然,如果只想快速上線,直接用現成的 SSG 方案也是完全沒問題的。

有任何想法或改進建議,歡迎聯絡我。下篇文章見!