首頁/產品

靜態網站產生器 Velo

2026年08月02日 產品 Velo Swift SwiftUI Cloudflare Pages 靜態網站 BLAKE3

同一個人維護好幾個靜態部落格,難的通常不是寫文章,是發佈。每個站各自一套建置流程,改一次版面就得重新搞懂一次:輸出目錄在哪、圖片要不要先壓、上次到底是怎麼傳上去的。現成的靜態網站產生器也不好用,要嘛設定檔要學一堆,要嘛想換個版面得先搞懂它那套樣板規則。

Velo 是一套 macOS App,只做一件事:選一個 Markdown 目錄、選一套範本、按一個按鈕,網站就上線。

Velo 長什麼樣子

Velo 的主畫面:左側專案清單,右側建置與發佈紀錄

畫面用 SwiftUI 打造,左邊是專案清單,一個部落格算一個專案;中間放這個專案的來源目錄、範本、輸出目錄;右邊是一顆「一次發佈」按鈕,下面接著即時紀錄和過去的發佈歷史。

按下去會一路做完:掃描 Markdown、解析 Front Matter、轉成 HTML、處理圖片、建立分類樹、套用範本、寫檔,最後上傳到 Cloudflare Pages。通常三十秒內就跑完。

Cloudflare 的 API Token 存在 Keychain,不會變成明文檔案躺在硬碟上;專案設定是一份 JSON,放在 ~/Library/Application Support/Velo/

三個網站

三個站是同一個 App、同一套流程做出來的,長得完全不一樣的原因只有一個:範本不同。目前實際在用的三個站:

木工

木工作品集的首頁

41 件作品,暖色系,標題用襯線字。這個站圖特別多,每篇都有一整套施作過程照片,輸出目錄 152 MB。分類只有「家具」和「生活小物」兩類,標籤則分得很細(榫接、手鉋、卯榫、拼板),瀏覽時多半靠標籤篩選。

攝影

攝影作品集的首頁,深色主題

48 篇,深色底配金色——照片放在深色背景上比較耐看。這是三個站裡最肥的一個,輸出 258 MB。

技術筆記

技術筆記的首頁

一樣 41 篇,但性質差很多:程式碼區塊多、圖少,整站只有 2.5 MB。分類有九類(系統與硬體、軟體專案、軟體開發、AI、資料庫、設計模式⋯⋯),這套範本把分類樹做得比較明顯。這篇文章,就是從這裡發佈出去的。

幾個設計上的重點

分類不用手動維護

Front Matter 沒寫 categories 的話,Velo 會直接拿資料夾結構當分類。來源目錄如果長這樣:

原始檔/
├── 系統與硬體/
│   └── 樹莓派/
│       └── 在 Raspberry Pi 5 上部署 PostgreSQL 17.md
├── 軟體專案/
└── 產品/
    └── 靜態網站產生器 Velo.md

巢狀資料夾會變成階層分類,像「系統與硬體/樹莓派」,首頁的分類樹自動長出來,可以展開收合,點了就篩選。想手動指定分類的文章照樣寫 categories,兩種方式並存。

圖片用內容雜湊命名

Markdown 裡的圖片路徑常常很亂:Typora 會把中文路徑寫成 percent-encoded 的樣子,有些舊文章甚至還留著 Windows 時代的絕對路徑。Velo 會用幾種方式輪流去找檔案,真的找不到就在紀錄裡留一行警告,不會整個建置停掉——少一張圖通常不是什麼大事,但建置中斷就得整個重跑。

找到之後,圖片會用內容雜湊重新命名再複製到輸出目錄。同一張圖被三篇文章引用只會存一份;改了圖,檔名跟著變,瀏覽器的快取自然就失效了。

圖片也可以改放 Cloudflare R2 或其他 S3 相容的儲存空間,AWS SigV4 簽章是自行實作的,沒有把 AWS SDK 拉進來。

範本故意做得很陽春

範本引擎只認四種語法:變數、原樣輸出的變數、條件、迴圈。沒有 helper、沒有 partial,也沒有繼承。

這是故意的設計。一套範本就是一份 index.html 加一份 post.html,CSS 和 JS 全部寫在裡面,因為建置只會輸出 HTML,範本資料夾裡的其他檔案不會被複製過去。想換風格就把整份 HTML 改掉,不用先學一套樣板語法。

上面三個站看起來差那麼多,差別就只在這兩個檔案。

發佈走 Cloudflare Pages 的 Direct Upload

官方文件對 Direct Upload 寫得不多,實際協定是照著 wrangler 的原始碼實作的,大致是:先拿 upload token,再問 Cloudflare「這批檔案你哪些還沒有」,只補傳缺的那些,接著送出完整的檔案清單,最後建立 deployment。

第二步是重點。攝影站有 258 MB,但改一篇文章通常只動到幾個檔案,伺服器已經有的就不用再傳一次,發佈時間因此從「等一段時間」變成幾秒鐘。

檔案識別碼不是 SHA-256,而是 BLAKE3:內容先 base64,接上副檔名,再取雜湊值的前 32 個 hex 字元。為了讓 Swift 這邊算出來的跟 wrangler 一模一樣,BLAKE3 的官方 C 實作被直接 vendor 進專案,並用官方測試向量驗證過。

現在

Velo 自己也簽章、公證過了,Developer ID 簽章、送 Apple 公證、stapler 蓋章都做完,下載下來直接開就好,不用再去「隱私權與安全性」按允許。

三個站現在的發佈流程是:文章寫完存檔、打開 Velo、按「一次發佈」,其餘的等它跑完就好。