iT邦幫忙

2026 iThome 鐵人賽

DAY 30
0
Modern Web

用 Astro 打造 Content-first 前端網站:30 天從靜態內容到會員、資料庫與選型(3rd)系列 第 30

30 天走完,什麼專案該選 Astro,什麼別硬上?

  • 分享至 

  • xImage
  •  

如果讀者來到網站主要是為了看文章、文件或商品資訊,搜尋、收藏、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 和頁面區塊。

  • 文章主體預渲染,只有登入、收藏或價格需要 request-time:仍然順著 Astro 的設計。
  • 每個主要畫面都依賴登入狀態、即時資料和 client state:Astro 能做,但它的預設優勢會逐步縮小。
  • 整頁最後包成一個大型 client:loadclient:only island:Astro 只剩外殼,主要產品其實已經是一個 Vue 或 React app。

Astro 官方也把 dashboard、inbox、social network、todo app 與 Figma-like UI 列為 application-focused frameworks 擅長的範圍。這些產品的共同點是:client app 與共享狀態本身就是主要畫面。

這個 capstone 能做什麼,邊界在哪裡?

只看 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 不會得到多少好處。

Island 的成本不只 bundle

Day 1 的同內容 benchmark量到:

頁面 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:loadclient: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 與同步就是架構逆風的訊號。

Astro、Nuxt、Next 的 client 邊界

三者都能做 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 生命週期內。

四題選框架,再檢查兩條實作邊界

拿到新專案時,可以依序問:

  1. 打開產品後,主要工作是先讀內容嗎?
    • 是:進下一題。
    • 否,主要工作是操作一個全畫面 reactive app:先比較 application framework。
  2. 大部分主要內容能在 build time 決定嗎?
    • 是:維持 prerender。
    • 否:先列出需要 cookie、DB 或即時資料的 request-time routes;若已占大多數頁面,再比較 output: server 與 application framework 的整體成本。
  3. 動態或個人化只占頁面一小塊嗎?
    • 是:考慮 server island 或局部 on-demand route。
    • 否,大多數 route 都 request-time:比較全站 server output 與其他 full-stack framework 的整體成本。
  4. Client 互動是局部 widget,還是產品的主要狀態模型?
    • 局部:用 Vue/React island,依操作優先級選 client:*
    • 全域、高頻、跨 route:不要把同一個 app 人工切成大量 islands。

決定使用 Astro 後,再檢查兩條實作邊界:

  1. Server 寫入是站內操作,還是公開 HTTP contract?
    • feedback、收藏、內部表單:用 Action,可以少寫 parsing 與型別接線。
    • webhook、第三方 client、自訂 headers/status:用 Endpoint 保留標準 RequestResponse
  2. 部署 runtime 真的支援 DB、auth、images 與 Node APIs 嗎?
    • 做過目標 runtime smoke,也就是在實際部署環境跑一次最小讀寫驗證,才算通過。
    • 只有 adapter build,就維持「待 runtime 驗證」;不要把「能編譯」當成「已跨平台上線」。

這套判斷不把「有沒有會員」當成分界。Day 24 的 Better AuthDay 29 的購物車已完成會員與寫入 demo;production runtime 仍有未驗證項目,列在後面的清單。

這些專案可以優先評估 Astro

  • 部落格、技術文件、新聞/出版、作品集與行銷頁。
  • 課程、活動、社群內容,以及內容先行的商品 catalog。
  • 團隊已有 Vue/React 元件,但只想把其中幾塊帶進內容頁。

內容來源也不必限定在 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 會依版面送出不同候選。

出現這些訊號,就先別硬上

  • 主要體驗是登入後 dashboard、inbox、社群 feed、協作編輯器、whiteboard 或 Figma-like UI。
  • 幾乎所有區塊都依賴同一份 client context、router 與高頻 global state。
  • 大多數 route 都 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 變小)。搬完後,島的範圍縮小,才值得比較不同的指令。

選 Astro 前,限制要一起簽收

  1. client:*、ClientRouter 與第三方 script 都會增加 browser JavaScript。
  2. 傳進 island 的 props 必須可序列化;大型資料應先投影,function 不能當作 server props 傳進 client。
  3. 跨 island 共用狀態需要額外的 store 與同步;整頁都在共享時,先檢查 island 是否切太碎。
  4. On-demand rendering 把成本移到 server,包括 DB latency、cache、secrets 與 observability。
  5. 更換 host 後,DB driver、auth、images 與 Node APIs 都要重新驗證。
  6. Build-time loader 的更新依賴 rebuild;request-time data 則承擔 runtime failure 與 cache 問題。
  7. 易變 API 寫作前要重查官方文件。這 30 天已經遇到 Astro DB 移除、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 只做選型判斷,沒有新增程式碼。


上一篇
內容站想長出購物車,讀寫該分別交給誰、又該在哪收手?
系列文
用 Astro 打造 Content-first 前端網站:30 天從靜態內容到會員、資料庫與選型(3rd)30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言