如果讀者來到網站主要是為了看文章、文件或商品資訊,搜尋、收藏、feedback、購物車只占局部,Astro 很合適。若主要畫面是一個長時間存活、跨 route 共享狀態的 client app,Nuxt 或 Next 往往比較省工。
會員、資料庫和 SSR 都能放進 Astro。判斷重點是:主要價值是否仍由內容與 server-rendered HTML 提供,以及瀏覽器需要長期接管多少畫面。前 29 天已完成 Content Collections、Vue islands、Actions、Endpoints、Drizzle + Turso、Better Auth 與購物車的實作或 demo,剛好能拿來檢查這條分界。
把 Astro 歸到靜態站、Nuxt 或 Next 歸到動態站,已經不足以描述現在的架構。
Astro 7 的官方定位仍是 content-driven、server-first 與 zero JavaScript by default;同時也有 on-demand rendering、Actions、middleware、sessions、server islands 與 adapters。預設 output: static 時,個別 route 可以用 prerender = false 改成收到 request 才算;如果大部分 route 都要即時渲染,也能改成 output: server。
判斷時,先標出動態功能落在哪些 route 和頁面區塊。
client:load 或 client:only island:Astro 只剩外殼,主要產品其實已經是一個 Vue 或 React app。Astro 官方也把 dashboard、inbox、social network、todo app 與 Figma-like UI 列為 application-focused frameworks 擅長的範圍。這些產品的共同點是:client app 與共享狀態本身就是主要畫面。
只看 Content Collections,容易把 Astro 縮成「Markdown 轉 HTML」。這個 capstone 加入 client、server、data 與 auth 後,實際邊界如下:
| 層 | 已完成的能力 | 實際邊界 |
|---|---|---|
| Content | 30 篇系列文章、schema、references、local/remote loaders、RSS、JSON | build-time loader 是 snapshot,來源更新後要重新 build |
| Client | 正式站有搜尋、ReactionButton、ClientRouter;feedback、登入、收藏、購物車仍在 demo routes | island 會帶入 framework runtime、component 與序列化 props |
| Server | 個別 on-demand routes、Actions、Endpoints、middleware/locals | route 需要 adapter 與 runtime;auth/data routes 另需 secrets 與 DB |
| Data | Drizzle + Turso/libSQL、feedback、favorite、cart schema | Workers 使用 HTTP client;Node 本機 smoke 不能代替 workerd 實測 |
| Auth | Better Auth、session middleware、登入與收藏 demo | GitHub OAuth、Turso cloud 與正式網域尚未上線驗證 |
| Deploy | Cloudflare adapter build 與本機 production preview | 尚未公開部署,也還沒有 Vercel fallback 實跑 |
Astro 能在內容站上逐步加入會員與資料層;每加入一層,也要承擔該層的 runtime 成本。prerender = false 仍需處理資料庫延遲、cache、secrets 與監控;更換 host 時,也要重測 DB driver、圖片服務與 Node APIs。
「有會員就不用 Astro」太粗糙。內容站可以有登入,dashboard 也可以有公開文件。比較有用的做法,是看兩個軸。
第一個是內容比重:使用者得到的價值,有多少來自可先產生、可 cache、需要快速抵達讀者的 HTML?文章、文件、商品說明、metadata 與公開頁面都算。
第二個是 client state 比重:畫面有多少區域必須長時間活在瀏覽器,頻繁更新,還要跨 route 或跨區塊共享同一份狀態?
| 內容比重 | Client state 比重 | 初步判斷 |
|---|---|---|
| 高 | 低 | Astro 的強項 |
| 高 | 中 | Astro + 局部 client/server islands + on-demand routes |
| 高 | 高 | Astro 仍可能合適,但要驗證 island payload 與共享狀態成本 |
| 低 | 高 | 先比較 Nuxt、Next 等 application framework |
這兩個軸是本文根據 Astro 的官方定位與前述實測整理出的判斷方式,不是官方門檻。
例如商品 catalog 的內容比重很高。商品名稱、圖片、說明與 SEO 可以先成為 HTML,登入、庫存或購物車再局部動態,這仍符合 Astro。協作白板則相反:主要價值就在游標、選取、畫布、即時同步與 undo stack;HTML 內容只占很小一部分,硬拆成 islands 不會得到多少好處。
| 頁面 | JavaScript gzip | FCP 中位數 |
|---|---|---|
| Astro 無島 | 0 B | 627 ms |
| Astro Vue island | 約 29 KB | 921 ms |
| Nuxt | 約 49 KB | 1353 ms |
這組數字只代表同一台機器、同一份小頁與當時版本下的結果,不是「Astro 永遠比較快」。三頁的 Lighthouse performance 都是 100;只看分數,甚至看不出 FCP 差異。正式 capstone 已經有 ClientRouter,文章頁也有 Vue island,所以不能把 bench 的 0 B 寫成目前整站現況。
這組數字能支持一個範圍較窄的結論:Astro 讓 JavaScript 成為顯式選擇。沒有 island 的頁面可以不載入 framework runtime;加入 island 後,頁面才承擔 component、props 與 hydration metadata 的成本。同一個 framework 的多座 islands 可以共用 runtime chunk,並非每座都重複下載完整 Vue。
Day 8 比較 client:load 與 client:visible又補了一條限制:兩者使用相同的 Vue component 與 renderer bundle,改變的是下載與 hydrate 時機,不是檔案大小。想省 bundle,得縮小 island 或減少依賴,不能只換 directive。
Server 傳給 island 的資料也要付費。Day 14 的搜尋實測在 13 篇文章下,只傳搜尋需要的五個欄位,產出的 HTML 是 30,140 bytes;把完整 data 與 body 都塞進 island,變成 161,300 bytes,相差 5.4 倍。Island 的邊界不只是 component 檔案,也包含你序列化過去的 props。
Astro 的優勢集中在這種局部互動。跨 island 共享狀態當然可做,官方也提供 Nano Stores 的做法;但如果幾乎每個區塊都要讀寫同一份 client state,額外的 store 與同步就是架構逆風的訊號。
三者都能做 SSR、SSG、動態 route 與全端功能。差別在於主要 UI 用什麼單位組成,以及 client 邊界從哪裡開始。
下表版本基準為 Astro 7.1.1、Nuxt 4、Next 16.2.9,官方文件查證日為 2026-07-24。定位、頁面主體、client 邊界與 UI 生態整理自官方文件;「常見起點」與「重新考慮的訊號」是本文根據架構邊界做出的選型推論。
| 面向 | Astro 7 | Nuxt 4 | Next App Router |
|---|---|---|---|
| 官方定位 | Content-driven、server-first | Vue full-stack app/website | React full-stack app |
| 頁面主體 | .astro server template,client JS 顯式 opt in |
Vue app context,route rules 可 prerender/cache | React Server Components + Client Components |
| Client 互動邊界 | client:* island |
Vue component/client-only boundary | 'use client' boundary |
| 常見起點 | 少數局部 widgets | Vue app 內廣泛共享狀態 | React app tree 內組合 server/client state |
| UI 生態 | 可混用 Vue、React 等,但每種 runtime 都有成本 | Vue 團隊最直接 | React 團隊最直接 |
| 重新考慮的訊號 | 每頁大型 island、到處共享 state、大多數 route on demand | 主要內容其實不需要整個 Vue app 接管 | 團隊不是 React,或 RSC/client 邊界不合現有模型 |
Nuxt 4也能依 route prerender 或 cache;Next則用 Server Components 控制進入 client bundle 的程式碼。差異落在團隊日常維護的單位:內容模板與少數 widgets 適合 Astro;完整 Vue/React app tree 通常更適合留在同一套 router、context 與 devtools 生命週期內。
拿到新專案時,可以依序問:
output: server 與 application framework 的整體成本。client:*。決定使用 Astro 後,再檢查兩條實作邊界:
Request/Response。這套判斷不把「有沒有會員」當成分界。Day 24 的 Better Auth與 Day 29 的購物車已完成會員與寫入 demo;production runtime 仍有未驗證項目,列在後面的清單。
內容來源也不必限定在 Markdown。Day 28 已把 Google Sheets 接進 Content Layer,同一個 Astro consumer 可以讀本機檔案、CMS 或 API。要先決定的是 build snapshot 還是 request-time data,不是「有 CMS 就不能靜態」。
Day 18 的 Astro 圖片最佳化與 Day 19 的響應式圖片實測也量過內容頁的圖片 pipeline。同一張圖從固定 720w/8,411 B,改成讓瀏覽器依版面選候選後,桌面三欄選 540w/5,264 B,390px 單欄選 360w/2,613 B。這些數字只適用於當時的本機 Cloudflare preview、Chrome 與 DPR 1,但足以確認 responsive image pipeline 會依版面送出不同候選。
prerender = false,每頁又掛接近整頁的大型 island。Astro 可以用 client:only 或大型 framework island 承載完整 SPA。當這種頁面成為常態,partial hydration 能省下的 JavaScript 已有限,團隊還要同時維護 Astro 與 client app 的生命週期。此時直接使用 Nuxt 或 Next,通常能減少跨邊界協調。
上面那三個訊號,可以在同一個真實專案上一次看到。
我複製了一個上線中的多語系品牌官網來量。它是 Astro 搭 Vue、四個語系、output: 'static'、沒有安裝 adapter。每條 route 的形狀都相同:一個 .astro 檔當殼,裡面掛兩三個 Vue 元件,全部 client:load;頁面的標題、文案與圖片都寫在那些 Vue 元件裡。全站出現九次 client:*,九次都是 client:load。
隔離掉兩個已停用、卻仍留在 src/pages/ 而導致 build 中斷的檔案後,build 產出 13 頁。首頁的量測結果:
| 量測 | 結果 |
|---|---|
dist/en/index.html |
7,262 B |
| 首頁靜態 import 圖的 JS | 215,091 B(6 支檔) |
| 其中最大一支 | 197,921 B(Vue runtime 與四語系翻譯訊息) |
| build 全部 JS | 268,482 B(57 支檔) |
首頁 <astro-island> 數量 |
2 |
這些數字是本機 astro build 的產物大小,不能當成實際傳輸量或整站效能結論。它們只支持一個較窄的判斷:這個專案的 HTML 幾乎不承載內容,內容要等那 210 KB 的 JavaScript 執行完才出現。表中的 197 KB 由所有島共用,並非每座島各自一份,符合前面提到的「多座 islands 共用 runtime chunk」。
這個專案是品牌官網加部落格,內容比重高,SEO 是主要目的之一;它沒有跨 route 共享的狀態,也沒有需要長期活在瀏覽器的畫面,因此 client state 比重低。照前面那張表,它落在「內容高、client state 低」,屬於 Astro 的強項。實際實作卻走到了「整頁包成大型 island」這條線上。
來路很順,跟框架選錯無關:團隊本來寫 Vue,src/views/<route>/index.vue 的目錄慣例是現成的,搬進 Astro 時最小的改動就是把整個 view 掛上來。在時間壓力下這是合理的第一步。
這個案例並不能推導出「不該選 Astro」;問題在於選用後沒有明確畫出 client 邊界。先把不需要互動的標題、文案與圖片搬回 .astro;只改 client:* 的值不會讓 bundle 變小(Day 8 量過那不會讓 bundle 變小)。搬完後,島的範圍縮小,才值得比較不同的指令。
client:*、ClientRouter 與第三方 script 都會增加 browser JavaScript。output: hybrid 移除、Cloudflare runtime env 路徑改變,以及 auth integration 停更。目前 capstone 已通過 Cloudflare adapter build、本機 production preview,以及 Node + local libSQL smoke;公開 Cloudflare deploy、Turso cloud、GitHub OAuth、真實 workerd HTTP transport 與 Vercel fallback 都還沒完成。這些缺口不推翻架構選擇,但目前不能把它當成已完成正式部署與 fallback 驗證。
選框架時,先看產品的主要狀態模型。內容先行、互動局部時,Astro 能讓 server-rendered HTML 與較少的 JavaScript 留在預設路徑上;若大部分畫面由跨 route 共享的 client state 驅動,Nuxt、Next 等 application framework 通常更省協調成本。會員、資料庫和 SSR 本身不是分界,效能也仍要回到實際頁面量測。
今日驗收:版本基準為 Astro 7.1.1;選型判斷以本文引用的前 29 篇實作與量測為限。公開 Cloudflare deploy、Turso cloud、GitHub OAuth、workerd HTTP transport 與 Vercel fallback 尚未列入已驗證範圍。
30 天的程式碼歷程在 app-steps,一天一個 commit,Day N 對應
step-N。Day 30 只做選型判斷,沒有新增程式碼。