Logo image
GitHub LinkedIn

【Astro】伺服器渲染策略與 Island Architecture

Relume placeholder avatar
張三

2022年1月11日

5分鐘閱讀

Relume placeholder image

【Astro】伺服器渲染策略與 Island Architecture

前言

在使用一個技術之前,最重要的是去了解這個技術的背景,以及這個技術的優缺點。但偏偏如 Next.js 或是 Astro 等框架解釋 Hydration 或是 SSR 的時候,牽涉到 Server 端、Client 端的互動,同時有很多抽象的名詞。因此想要寫一篇清楚的筆記,來研究這些框架的設計理念。

前端框架的困境

雖然有點繞口,不過前面還是要補充一點, Next.js 或是 Astro 等框架出現的背景。

傳統服務端渲染時代

SSR 傳統服務端渲染流程

🗄️ Database🖥️ Web Server🌐 Browser (Client)👤 User🗄️ Database🖥️ Web Server🌐 Browser (Client)👤 User傳統 Server Side Rendering (PHP 時代)用戶每次互動都需要重新請求整個頁面1. 點擊連結或輸入網址2. HTTP 請求頁面3. 查詢資料庫4. 回傳資料5. Server 端處理邏輯生成完整 HTML6. 回傳完整 HTML + CSS7. 立即顯示完整頁面 ✅8. 點擊另一個連結9. 新的 HTTP 請求10. 重新查詢資料11. 回傳新資料12. 重新生成新頁面 HTML13. 回傳新的完整 HTML14. 頁面重新載入顯示 🔄
  • PHP & 模板引擎:依靠 server 端輸出完整的 HTML 結構
  • 優點:SEO 友好,首次載入即可看到完整內容
  • 缺點:使用者體驗較慢,每次互動都需要等待 Server 端重新渲染,缺乏現代 Web 應用的流暢性

客戶端渲染時代

什麼是 Virtual DOM?

React 的本質是透過 React.createElement 來建立虛擬的 DOM 結構,並且透過 ReactDOM.render 比較前後兩個虛擬 DOM 的差異,來渲染到 DOM 上,而這件事情是在 Client 端執行。

// 建立一個 div 元素,並且在裡面放上 "Hello World" 文字
React.createElement("div", null, "Hello World");
// 在 index.html 中,會有一個 id 為 root 的 div 元素,並讓 React 接管這個元素的
DOM 操作

<!DOCTYPE html>
<html lang="zh-TW">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
  </head>
  <body>
    <!-- React 接管的根元素 -->
    <div id="root"></div>
  </body>
</html>

CSR 客戶端互動範例

🎨 Real DOM🔄 Virtual DOM⚛️ React App🌐 Browser👤 User🎨 Real DOM🔄 Virtual DOM⚛️ React App🌐 Browser👤 UserCSR 客戶端互動流程整個過程在 Client 端完成,無需 Server 請求1. 點擊按鈕2. 觸發 onClick 事件3. 執行 setState()4. 創建新的 Virtual DOM Tree5. Diff 算法比較前後差異6. 僅更新變化的 DOM 節點7. 頁面立即更新8. 用戶看到即時反饋 ✨
  • React 等 SPA 框架:使用 Virtual DOM 來管理 DOM 操作,提供更流暢的使用者互動體驗
  • 優點:使用者可以更快速得到反饋,無需頁面重新載入,提供類似原生應用的體驗
  • 缺點
    • Virtual DOM 不利於 SEO,搜索引擎爬蟲難以解析 JavaScript 生成的內容
    • 首次載入時間較長,需要下載並執行 JavaScript 才能看到內容
    • 對於低效能設備或網路環境較差的使用者體驗不佳

現代解決方案:Hydration & SSR

根據 And Now You Understand React Server Components | Kent C. Dodds 的說法,RSC 的本質就是提前在 Server 端執行 React.createElement() 的方法,並提前完整產生 HTML( Hydration ),這樣 SEO 就能解析到<div id="root"/> 底下完整的內容。

  • 核心概念:就像「脫水」與「補水」的過程
    1. Server 端:預先產生完整的 HTML 結構(脫水狀態)
    2. Client 端:瀏覽器載入 JavaScript 後重新綁定事件監聽器和互動邏輯(補水過程)
  • 優勢結合
    • 保持 SEO 友好性(完整的 HTML 結構)
    • 提供快速的首次內容呈現(First Contentful Paint)
    • 維持現代 Web 應用的互動體驗

前端渲染策略比較表

同時根據資料的新鮮度,也會採取不同的渲染策略。

項目ISRSSGSSRCSR
資料更新頻率中頻率更新低頻率更新高頻率更新即時更新
首次載入速度極快極快
SEO 友好度優秀優秀優秀
伺服器負載極低極低
適用情境部落格、商品頁面文檔、行銷頁面社交媒體、即時數據管理後台、互動應用
代表框架Next.js ISRGatsby、AstroReact Server ComponentReact Client Component

Astro vs Next.js

Next.js

NextJS 是基於 RSC (React Server Component) 的概念,來實現 Progressive Hydration、<Suspense> 實現 Partial Hydration。

💡 Progressive Hydration: 基於時間、優先級或使用者行為,逐步水合整個應用程式。

💡 Partial Hydration: 只水合頁面中需要互動的特定部分,其餘保持靜態。

NextJS 的 SSR + CSR 混合架構

📡 API Server🖥️ NextJS Server🌐 Browser (Client)👤 User📡 API Server🖥️ NextJS Server🌐 Browser (Client)👤 UserNextJS App Router (SSR + CSR 混合)🖥️ Server Side Phase🌐 Client Side Phase (Hydration)🔄 Client Navigation (CSR)1. 訪問頁面2. 請求頁面3. Server Component 取得資料4. 回傳資料5. RSC 執行 React.createElement()6. 回傳完整 HTML + 頁面資料 ✅ SEO 友好7. 立即顯示靜態內容8. 下載 JavaScript Bundle9. 回傳 JS Bundle10. Progressive Hydration 開始11. 綁定事件監聽器到已存在的 DOM12. 頁面變為完全互動 ✅ 使用者體驗佳13. 點擊連結 (client 導航)14. Client Component 請求新資料15. 回傳 JSON 資料16. Virtual DOM 更新17. 即時頁面更新 ✅ 無頁面重新載入
// Server Side Component (預設)
async function ServerComponent() {
  const data = await fetch("api/data"); // 在 Server 端執行
  return <div>{data}</div>; // HTML 直接生成
}

// Client Side Component (需要 "use client" 指令)
("use client");
function ClientComponent() {
  const [count, setCount] = useState(0); // 在 Client 端執行
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

Astro

Island Architecture (Partial Hydration): 本質上與 Progressive Hydration 的處理方式是類似的,但差別是 Island Architecture 將元素拆成獨立的 HTML 片段,並在必要的時候使用 JavaScript 來處理互動。

💡 Partial Hydration: 只水合頁面中需要互動的特定部分,其餘保持靜態。

Astro 的 Island Architecture

🗺️ Island Architecture 特色

靜態內容

SEO 友好

快速載入

互動島嶼

按需載入 JS

漸進增強

🌊 靜態 HTML 海洋

Main Content Area

🏝️ Sidebar

純 HTML + CSS

導航連結

🏝️ Interactive Island

React/Vue Component

client:load

🏝️ Header Island

純 HTML + CSS

無 JavaScript

🏝️ Footer

純 HTML + CSS

無 JavaScript

---
// 僅在需要的時候使用 Javascript 程式碼
---

<div>
  <h1>Hello World</h1>
  <p>This is a paragraph</p>
</div>

<MyReactComponent client:load />

TD;LR

探索更多精彩內容

繼續閱讀,了解更多技術與個人經歷的精彩文章。