cover

從 Hexo 到 Quartz:為什麼搬家、怎麼搬、學到了什麼

如果你跟我一樣,部落格用 Hexo 跑了好一陣子,文章也累積了兩百多篇,某天突然覺得「這堆文章之間怎麼毫無關係?」——那這篇文章就是寫給你的。

這篇會分享我從 Hexo 搬到 Quartz 的完整過程:為什麼要搬、怎麼選的、搬家踩了哪些坑、搬完以後有什麼改變,以及一些技術細節和給同樣想搬家的人的建議。


一、為什麼要搬家

先說結論:不是 Hexo 不好,而是我的需求變了。

1.1 文章之間缺乏連結

Hexo 的文章本質上是一篇一篇獨立的 blog post。你寫了一篇 JavaScript 閉包,又寫了一篇 React Hooks,這兩篇文章之間除了你自己腦中知道有關聯以外,在網站上完全看不出來。

讀者看完一篇文章,能做的事只有「回到首頁看列表」或「點分類/標籤」。但分類和標籤終究只是標記,不是真正的知識連結。

1.2 知識是列表,不是網

Hexo 的首頁就是一個按時間排序的文章列表。這對新聞型部落格來說很合理,但對技術筆記來說是災難。我越來越覺得,知識應該是「網狀」的——A 引用 B,B 又連到 C,讀者可以順著脈絡探索。

1.3 Theme 維護令人崩潰

Hexo 的 theme 生態看起來很豐富,但實際上:

  • 很多 theme 已經停更了
  • 想改一個小地方要翻一堆 EJS/Pug 模板
  • 升級 Hexo 版本 theme 就爛掉
  • 不同 theme 的 config 格式不統一

我花在「讓部落格看起來沒壞掉」的時間,比花在「寫文章」的時間還多,這本末倒置了。


二、為什麼選 Quartz

在決定搬家之前,我認真比較了幾個主流選項:

特性QuartzHugoGatsbyDocusaurusObsidian Publish
Wikilinks 支援原生支援需外掛需外掛不支援原生支援
Graph View內建內建
Obsidian 格式相容完整部分部分不支援完整
建置速度極快中等N/A(雲端)
自訂彈性高(TypeScript)高(Go Template)極高(React)中等
部署成本免費(GitHub Pages)免費免費免費付費(/月)
學習曲線中等中等極低
反向連結內建需外掛內建

Quartz 的殺手功能

最後讓我選 Quartz 的三個關鍵理由:

1. Wikilinks

[[文章名稱]] 就能建立文章之間的連結。不用記 URL、不用翻 slug,寫作的時候想到相關文章就直接連過去。這完全改變了寫作的心態——從「寫完一篇是一篇」變成「每篇都是知識網路的一個節點」。

2. Graph View(知識圖譜)

每篇文章右側都有一個互動式的知識圖譜,可以看到這篇文章和哪些其他文章有關聯。讀者可以拖拉、縮放,順著圖譜探索整個知識庫。這不是花俈的裝飾,是真的有用。

3. Obsidian 格式全面相容

我平常就用 Obsidian 寫筆記,Quartz 直接吃 Obsidian 的 Markdown 格式(包括 callout、wikilink、嵌入等)。寫作流程從「Obsidian 寫 → 轉格式 → 發布」變成「Obsidian 寫 → 發布」。


三、搬家過程

搬兩百多篇文章聽起來很恐怖,但其實拆開來看每一步都不難。

搬家流程圖

flowchart TD
    A[Hexo source/_posts/] --> B[分析 frontmatter 格式]
    B --> C[AI 批次轉換 frontmatter]
    C --> D[處理圖片路徑]
    D --> E[重新設計分類結構]
    E --> F[加入 wikilinks 交叉引用]
    F --> G[品質篩選與分級]
    G --> H{品質 OK?}
    H -->|是| I[放入 Quartz content/]
    H -->|否| J[標記為 draft 或刪除]
    I --> K[設定 quartz.config.ts]
    K --> L[本地預覽測試]
    L --> M[部署到 GitHub Pages]

    style A fill:#d79921,color:#282828
    style M fill:#98971a,color:#282828
    style J fill:#cc241d,color:#fbf1c7

3.1 Frontmatter 轉換

Hexo 和 Quartz 的 frontmatter 格式有些差異。主要差異:

  • abbrlink 在 Quartz 中不需要(Quartz 用檔案路徑當 URL)
  • index_img 要換成 Quartz 的圖片處理方式
  • categories 的巢狀陣列要攞平
  • date 格式建議統一成 YYYY-MM-DD

3.2 圖片路徑大搬遷

這是最惱人的一步。我的做法是把所有圖片集中到 content/static/images/ 下,按分類建子資料夾,用腳本批次替換文章中的圖片路徑。

3.3 分類結構重新設計

搬到 Quartz 以後,因為有了 wikilinks 和資料夾結構的天然支援,我重新設計了分類。每個資料夾就是一個知識領域,資料夾裡可以有 index.md 當作該領域的入口頁面。

3.4 兩百多篇品質參差:怎麼辦?

我分了三個等級:

  • A 級(直接搬):內容完整、有參考價值的文章,約 60 篇
  • B 級(改了再搬):主題有價值但需要重寫或補充,約 80 篇
  • C 級(先不搬):過時、太短、或已被其他文章覆蓋的

先把 A 級搬過去讓網站能用,B 級慢慢改,C 級看心情。

3.5 AI 輔助批次轉換

手動改兩百多篇 frontmatter?別鬧了。我用 AI 寫了腳本來批次處理:Frontmatter 轉換、自動補 description、分類映射、Wikilink 建議。AI 省了我大概 80% 的時間。


四、搬完之後的改變

搬完之後最大的感受是:終於覺得自己的部落格是一個「知識庫」而不只是一堆文章的集散地。

4.1 文章有「關係」了

以前寫完一篇就是一篇,現在寫完一篇會自然地想:這篇跟哪些已有的文章有關聯?該加哪些 wikilinks?

舉個例子,我寫一篇 React useEffect 的文章時,會順手加上 [[javascript/closure|JavaScript 閉包]][[react/hooks-overview|React Hooks 總覽]] 的連結。讀者看完 useEffect,可以直接跳到閉包的原理,或是回到 Hooks 的全局視角。

4.2 寫作方式改變

在 Hexo 時代,我寫文章的心態是「產出一篇完整的東西」。在 Quartz 之後,變成「在知識網路上新增一個節點」。這個轉變聽起來很小,但實際影響很大:

  • 不再害怕寫短文章:一個概念值得一篇獨立的筆記,即使只有 200 字,因為它可以被其他文章引用
  • 更願意回頭更新舊文:因為舊文被新文連結引用了,維護它就有了動力
  • 知識累積有複利效應:文章越多,彼此之間的連結越密集,整體價值就越高

4.3 跨系列引用變得自然

以前在 Hexo 要做跨系列引用,得手動複製 URL、寫 [連結文字](超長路徑)。現在只要 [[文章名稱]],甚至可以用 [[文章名稱|顯示文字]] 自訂顯示。

這個改變看似微小,卻讓我「願意」去做交叉引用。以前嫌麻煩就不連了,現在隨手就連。


五、Quartz 技術細節

這一節給想深入了解 Quartz 的人。

5.1 quartz.config.ts

Quartz 的核心設定檔是 quartz.config.ts,用 TypeScript 寫的,有型別提示,改起來比 Hexo 的 _config.yml 舒服很多。

幾個重要設定:

const config: QuartzConfig = {
  configuration: {
    pageTitle: "你的網站名稱",
    enableSPA: true,           // 單頁應用模式,頁面切換更流暢
    enablePopovers: true,      // hover 預覽連結內容
    locale: "zh-TW",           // 繁體中文
    baseUrl: "your-username.github.io",
  },
  plugins: {
    transformers: [
      Plugin.ObsidianFlavoredMarkdown({
        enableInHtmlEmbed: false,
      }),
      Plugin.CrawlLinks({
        markdownLinkResolution: "shortest",  // wikilink 解析策略
      }),
    ],
  },
}

CrawlLinksmarkdownLinkResolution: "shortest" 是個很重要的設定。它讓你用最短的檔名就能連結到文章,不用寫完整路徑。例如 [[closure]] 就能連到 javascript/closure.md

5.2 Layout 自定義

Quartz 的 layout 設定在 quartz.layout.ts,可以調整每個頁面區塊要放哪些元件:

export const defaultContentPageLayout: PageLayout = {
  beforeBody: [
    Component.ArticleTitle(),
    Component.ContentMeta(),
    Component.TagList(),
  ],
  left: [
    Component.PageTitle(),
    Component.Search(),
    Component.Explorer(),       // 檔案瀏覽器
  ],
  right: [
    Component.Graph(),          // 知識圖譜
    Component.TableOfContents(),
    Component.Backlinks(),      // 反向連結
  ],
}

Graph 元件可以進一步調整節點大小、連結距離、是否顯示標籤等。Backlinks 元件也可以設定是否顯示引用上下文(showContext: true),讓讀者不用點進去就能看到「哪篇文章的哪段話引用了這篇」。

5.3 部署方式

我用 GitHub Actions 自動部署到 GitHub Pages。流程很簡單:

  1. 推 code 到 main branch
  2. GitHub Actions 自動跑 npx quartz build
  3. 產出的靜態檔案部署到 GitHub Pages

Quartz 官方就有提供 GitHub Actions 的範本,幾乎不用改就能用。

5.4 常踩的坑

搬家過程中遇到一些值得注意的問題:

  • 中文檔名要小心:雖然 Quartz 支援中文檔名,但建議還是用英文,避免 URL encode 造成的各種問題
  • 圖片路徑是相對於 content 根目錄:不是相對於文章所在位置,這跟 Hexo 不一樣
  • Mermaid 圖表要注意中文字元:有些特殊標點在 Mermaid 裡會造成渲染錯誤
  • frontmatter 的 date 格式:建議統一用 YYYY-MM-DD,不要用 YYYY/MM/DD 或帶時間的格式
  • SPA 模式下的外部連結:開了 enableSPA: true 以後,要確認外部連結有加 target="_blank"

六、給想搬家的人的建議

如果你也在考慮從 Hexo(或其他 SSG)搬到 Quartz,這是我的幾點建議:

6.1 不用一次搬完

最大的錯誤就是想一次把所有文章都搬過去。你會被海量的格式轉換淊沒,然後放棄。

我的建議是:先搬 10-20 篇你最滿意的文章,把網站先架起來。確認一切正常後,再慢慢搬剩下的。

6.2 先搬結構,再提升品質

先把資料夾結構和分類設計好,把文章放進對應的位置。內容品質可以之後再慢慢提升。結構對了,後面改內容就輕鬆很多。

6.3 善用 AI 輔助

重複性高的工作(frontmatter 轉換、格式修正、補 description)讓 AI 來做。把你的時間花在「重新組織知識架構」和「建立文章之間的連結」這些 AI 做不好的事情上。

6.4 搬家是重新整理知識的好機會

不要把搬家當成苦差事。這其實是一個很棒的機會,讓你重新審視自己寫過的東西:

  • 哪些文章已經過時了?
  • 哪些主題值得擴充?
  • 哪些文章之間其實有關聯,只是以前沒有連起來?
  • 你的知識體系有哪些缺口?

搬完以後你會對自己的知識庫有一個全新的認識。

6.5 記得設定 redirect

如果你的舊部落格有被搜尋引擎收錄,搬家後記得做好轉址。可以在 GitHub Pages 上設定 _redirects 檔案,或是用 meta refresh 的方式把舊 URL 導向新 URL。不然你辛苦累積的 SEO 就白費了。


結語

從 Hexo 搬到 Quartz,表面上是換了一個靜態網站產生器,實際上是改變了我對「部落格」這件事的理解。

部落格不只是「按時間排列的文章列表」。它可以是一個活的知識網路,文章之間互相引用、互相補充,讀者可以順著自己的興趣和脈絡探索。

搬家的過程確實花了不少時間,但搬完以後回頭看,這是一個非常值得的投資。如果你的部落格也累積了一定數量的文章,而且你希望這些文章能產生「1+1>2」的效果,那 Quartz 值得你認真考慮。


延伸閱讀