iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0
Modern Web

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

astro 元件跟 React 元件差在哪?為什麼它預設不送 JavaScript?

  • 分享至 

  • xImage
  •  

Day 4 一直在講 .astro:layout 是 .astro、頁面是 .astro、被包進去的東西也是 .astro。但它到底是什麼?如果你寫過 React 或 Vue,它看起來很眼熟,眼熟到你會直接套用舊直覺,然後在某個地方踩空。

.astro 元件收 props、能拆分組合,這些跟 React 元件一樣。差別在執行位置:它在建置(或收到請求)時跑一次,交出 HTML,然後就不存在了。瀏覽器只收到它產出的 HTML,不會收到元件本身的 JavaScript;也就沒有由它維護的 state、re-render 或點擊事件。React 的心智模型是「元件是一段會在瀏覽器裡跑的程式」;Astro 的心智模型是「元件是一份會產出 HTML 的模板」。

相同之處:props

.astro 元件怎麼收資料,跟 React、Vue 幾乎一模一樣。用 interface Props 宣告要收什麼,再從 Astro.props 解構出來,還能給預設值。官方文件的最小範例長這樣:

---
interface Props {
  name: string;
  greeting?: string;
}
const { greeting = "Hello", name } = Astro.props;
---
<h2>{greeting}, {name}!</h2>

(來源:Astro 官方 Astro Components 文件,查證日 2026-07-21。)

greeting? 後面的問號代表選填,= "Hello" 是呼叫端沒給時的預設值。你在 React props 和 Vue 的 defineProps 都見過這套「型別宣告 + 解構 + 預設值」,概念一致。

Day 4 看過的 BaseLayout.astro 就是「一個會收 props 的 .astro 元件」:

---
interface Props {
  title: string;
  description?: string;
  type?: 'website' | 'article';
  // …還有 image、article
}
const { title, description = SITE.description, type = 'website' } = Astro.props;
---

title 必填、其餘選填,description 沒給就退回站台預設。就是 props,沒有新東西。

執行位置的差別:它不在瀏覽器裡跑

props 的寫法很熟悉,但執行位置不同:.astro 元件的邏輯跑在建置或伺服器端,不在瀏覽器。官方文件寫得很直接:

By default, Astro renders UI components to static HTML and CSS, stripping all client-side JavaScript.
(預設情況下,Astro 把 UI 元件渲染成靜態 HTML 與 CSS,並剝掉所有客戶端 JavaScript。)

來源:Astro 官方 Islands 文件,查證日 2026-07-21。

寫在 .astro 那對 --- 之間的 JavaScript,只在「產 HTML 的時候」執行。執行完後只有 HTML 送到瀏覽器,所以這個元件無法在瀏覽器裡處理 state、useStateonClick,也沒有重新渲染。

實作專案裡的 src/components/BenchArticle.astro 是一個純內容樣本:

---
// frontmatter 只有註解,沒有 props、沒有 script
---
<article>
  <h1>手沖咖啡的三個入門變數</h1>
  <p>……純內容……</p>
  <ul>
    <li>……</li>
  </ul>
</article>

BenchArticle.astro 只負責顯示文章,本來就不需要互動。它會產出 <article>,送到瀏覽器的 JavaScript 是 0 bytes;它就是一個完成的純內容元件。

React 元件會在瀏覽器裡持續運作;.astro 元件產出 HTML 後就結束,預設不留任何 JS。React 開發者讀 Astro 時,先把這個直覺調過來。

什麼時候需要 JS?純顯示通常不用

只要元件的工作是「把資料顯示出來」,它就不需要 JS。一張卡片、一段文章、一個 header、一個文章列表,這些都是純顯示,.astro 元件配零 JavaScript 就做完了。BenchArticle 就是這種。

需要「瀏覽器端的互動」時才送 JS,例如使用者點一下要有反應、要記住某個狀態,或要即時更新畫面。這類區塊用框架元件(Vue 或 React)來做;Day 8 會再談 islands。

你沒辦法在 .astro 元件上加 client:loadclient:* 這類「要不要在瀏覽器啟動」的指令,只對框架元件有意義;.astro 元件不會在瀏覽器裡啟動,所以沒有這個選項。判斷一塊 UI 要不要送 JS,實際上就是在選 .astro 元件或框架 island。

專案裡的 ReactionButton 可以看出 JavaScript 是「一顆一顆元件選進來的」。在 bench/index.astro 寫成 <ReactionButton />,不加 client:,它就是靜態的、按鈕點不動、零 JS;在 bench/island.astro 寫成 <ReactionButton client:load />,加上指令才會啟動、送 JS,按鈕也才能點。這組對照會在 Day 8 再拆解。Astro 預設不會把 JavaScript 鋪滿整頁。

常見誤區

  • 想在 .astro frontmatter 裡用 windowdocumentonClick 這段程式在伺服器端執行,當下沒有瀏覽器或 DOM。操作 DOM 是 Day 7(頁面內的 <script>)或 Day 8(island)的事。
  • 想讓 .astro 元件「加上」互動。 需要互動時,把那個區塊換成框架元件,再用 client:* 啟用客戶端 JavaScript。.astro 元件本身沒有「變得能互動」這個路徑。
  • 以為零 JS 是功能少。 零 JS 是 Astro 的預設,也是效能起點。一個網站大部分的元件本來就只需要顯示;能顯示就用 .astro,把 JS 留給真正需要的地方。

今日驗收

.astro 元件跟 React 元件都用 props 傳資料,生命週期卻不同:.astro 元件跑一次、交出 HTML 就結束,瀏覽器裡不留 JS。面對一塊 UI,先問它是只要顯示,還是需要互動。只要顯示就用 .astro 元件配零 JS,需要互動才做成框架 island。.astro 元件不能加 client:*,因為它不在瀏覽器裡跑。

下一步

分清楚零 JS 的 .astro 元件後,下一個問題是樣式:CSS 寫在元件裡,會不會漏出去污染別的頁面?什麼時候該用共用的全域樣式?Day 6 會談 Astro 的樣式分層:scoped、global 還是 utility,以及各自適合放在哪裡。

本日程式碼:step-05|只看這天的改動:step-04...step-05


上一篇
每一頁都要 header、footer,Astro 怎麼讓我只寫一次?
下一篇
元件元件裡的 CSS 會不會漏出去污染別頁?Astro 樣式怎麼分 scoped、global、utility?
系列文
用 Astro 打造 Content-first 前端網站:30 天從靜態內容到會員、資料庫與選型(3rd)9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言