Article

從 Vite 走到部署:試著理解 VoidZero 的 Void

整理 Void 的 SDK、CLI 與自架平台各自負責什麼,並從個人 Astro 網站的角度,思考「從程式碼推論資源」這種部署方式的好處與代價。

本文目錄

前端工具這幾年最明顯的變化,是「建置」這件事愈來愈少需要被意識到。Vite 讓開發伺服器幾乎即開即用,Rolldown 與 Oxc 又把打包、lint、格式化往同一套工具鏈收攏。可是當專案需要資料庫、儲存空間或排程時,開發者還是得從 vite build 的輸出出發,自己接上另一個世界:雲端主控台、設定檔、權限與 migration。

VoidZero 在 2026 年 10 月公開的 Void,想處理的正是這道縫隙。這篇文章不打算寫成完整教學,而是整理我目前對 Void 的理解:它由哪些部分組成、核心想法是什麼,以及對一個像本站這樣的個人 Astro 網站而言,它可能帶來什麼、又會要求我交出什麼。

先釐清 Void 的位置

討論一個新工具之前,先確認它站在哪一層,比急著比較功能更有幫助。Void 不是另一個打包器,也不只是一個託管服務,而是試圖把「寫 server 端程式」與「把它部署出去」接在同一條路徑上。

VoidZero 與 Cloudflare

VoidZero 是 Vite、Vitest、Rolldown 與 Oxc 背後的公司。2026 年 6 月 Cloudflare 宣布收購 VoidZero,整個團隊加入 Cloudflare,並承諾 Vite 等專案維持 MIT 授權與廠商中立。

這個背景會直接影響 Void 的形狀。根據 Cloudflare 的發表文章,團隊原本規劃的是一個私有的託管平台,加入 Cloudflare 之後才改為完全開源的方向。結果就是:Void 本身採 MIT 授權,但它的執行環境明確建立在 Cloudflare Workers 上。

三個部分,一個套件

Void 由三個部分組成,卻以同一個 npm 套件與 Vite plugin 發布:

  • Void SDK:給全端應用使用的 JavaScript SDK,包含檔案式路由、loader 與 action、資料庫、KV、物件儲存、AI、佇列與排程等功能。
  • Void CLI:用 Vite 建置應用、套用 migration、建立所需的雲端資源,最後部署出去。
  • Void Platform:可以安裝在團隊自己 Cloudflare 帳號裡的共用部署環境,負責登入、權限、資源上限與使用量。

這三者的關係可以簡單理解成:SDK 決定程式「需要什麼」,CLI 負責「把它變成真的」,Platform 則回答「誰可以這麼做」。

從程式碼推論資源

Void 最值得討論的地方,不是它支援哪些框架,而是它處理基礎設施的方式。傳統流程裡,開發者先在主控台或設定檔宣告一個資料庫,再把 binding 名稱寫進程式;Void 則把順序反過來,從程式匯入了什麼去推論需要哪些資源。

一個 import 等於一份宣告

以資料庫為例,在頁面的 server 檔案裡匯入 void/db:

TypeScriptpages/blog.server.ts
UTF-8|9 Lines|
import { defineHandler, type InferProps } from 'void';
import { db } from 'void/db';
import { posts } from '@schema';

export const loader = defineHandler(async () => ({
  posts: await db.select().from(posts),
}));

export type Props = InferProps<typeof loader>;

開發時,Void 會提供一個本機資料庫;執行 void deploy 時,則替這個專案建立 Cloudflare D1,並套用 migration。同樣地,匯入 void/ai 會設定 AI Gateway,crons/ 與 queues/ 目錄裡的檔案則對應排程與佇列。

InferProps 讓 loader 的回傳型別直接成為頁面元件的 props。從 Drizzle schema、server loader 到頁面,型別可以沿著同一條路流下來,不需要另外維護一份 API 型別。

這種設計換來什麼

我覺得這個想法最有吸引力的地方,是它讓「程式碼」與「基礎設施描述」不再是兩份需要手動同步的文件。新增功能時不必先離開編輯器開主控台,移除功能時,也比較不容易留下沒人記得用途的資源。

它也很適合現在常見的另一種寫程式方式。void init 會在新專案裡放一份精簡的 AGENTS.md,而 Cloudflare 在收購時提到的理由之一,就是 AI 產生的應用愈來愈常預設使用 Vite。當程式碼可能由 agent 寫出來時,「寫了 import 就能部署」的路徑確實比「請先到某個主控台建立資源」順暢許多。

隱性宣告的代價

不過,把宣告藏進 import 也有另一面。當基礎設施由程式碼「推論」出來,讀程式的人就需要知道哪些 import 具有副作用。一行看似普通的 import { kv } from 'void/kv',實際上可能在部署時多建立一個雲端資源。

這讓我想起本站在內容模型上的選擇:寧可讓 draft 成為必填欄位,也不要依賴「沒填就當成公開」。明確的宣告寫起來比較囉唆,卻能讓幾個月後的自己更快理解發生了什麼。Void 的推論能減少重複,但在團隊或長期維護的專案裡,或許仍值得在 README 或 review 流程中,把「這次部署會新增哪些資源」說清楚。

放進 Astro 網站裡看

理論討論完,最實際的問題是:它能不能放進我現在的網站?Void 提供 Astro 的整合方式,框架保留自己的路由與渲染,Void 只負責資源與部署。

整合方式

Astro 仍然使用 @astrojs/cloudflare adapter,Void 則以 Vite plugin 的形式加入:

JavaScriptastro.config.mjs
UTF-8|8 Lines|
import cloudflare from '@astrojs/cloudflare';
import { defineConfig } from 'astro/config';
import { voidPlugin } from 'void';

export default defineConfig({
  adapter: cloudflare({ configPath: './.void-wrangler.jsonc' }),
  vite: { plugins: [voidPlugin()] },
});

另外需要一份 void.config.ts,設定 Workers 名稱、compatibility date,並加入 nodejs_als flag。之後就能在 API route 裡直接使用 void/db 這類匯入。

對本站而言的判斷

本站目前是一個靜態網站:內容來自 Sanity,建置時取得資料,產出 HTML 後部署到 Vercel。整個網站沒有需要在請求時讀寫的資料庫,也沒有排程或佇列。

用前面的問題來檢查,答案其實相當清楚:

  1. 網站是否需要 server 端資料?目前不需要。
  2. 是否需要使用者產生的資料,例如留言或按讚?還沒有。
  3. 是否願意把部署平台從 Vercel 換到 Cloudflare?要看前兩題。

前兩題都是「否」時,Void 帶來的主要價值,也就是資源推論與型別貫通,幾乎都用不上。對純靜態網站而言,換一個部署目標本身不算是改進。

但如果有一天想加入文章的閱讀計數、簡單的留言,或是定期整理 RSS 的排程,情況就不同了。那時候 Void 讓「在 Astro 裡加一個 API route 和一張資料表」變成單一 repository 內的事,而不是再開一個後端服務。這比較像是一個值得先記下來的選項,而不是現在就該遷移的理由。

值得持續觀察的地方

Void 還很新,很多判斷需要時間驗證。目前我會特別留意幾件事:

  • 對 Cloudflare 的綁定程度:Void 本身開源,但 runtime 明確以 Workers 為前提。SDK 的抽象未來是否能接上其他平台,會影響它是「Vite 生態的部署層」還是「Cloudflare 的 Vite 入口」。
  • 推論規則的可見性:部署前能否清楚預覽將要建立或修改的資源,會決定這套機制在正式專案裡有多讓人安心。
  • 自架平台的門檻:Void Platform 需要 Workers for Platforms、R2、PostgreSQL、登入服務與網域。對公司內部平台很合理,對個人使用者則明顯偏重。
  • 框架整合的完整度:以 Astro 為例,登入功能仍需另外處理。框架模式與 Void 原生頁面之間的功能差距,會影響已有專案的採用意願。

小結

Void 讓我覺得有趣的地方,在於它把一個常被分開處理的問題重新放回同一處:程式需要什麼,就由程式自己說出來。這和 Vite 當初讓開發伺服器「不用設定也能用」的精神一脈相承,只是對象從打包換成了雲端資源。

對本站這種內容網站,現在還不是使用它的時候。但當網站開始需要一點點 server 端的能力時,我會想先回來看看 Void,而不是直接再架一個後端。

參考資料