---
# System prepended metadata

title: 前端效能優化 Day24、25 X Web Rendering Architectures & ESR

---

# 前端效能優化 Day24、25 X Web Rendering Architectures & ESR

[https://ithelp.ithome.com.tw/articles/10279519](https://ithelp.ithome.com.tw/articles/10279519)

### 比較表

|  | CSR | SSR (混合式) | SSG | ISR |
| --- | --- | --- | --- | --- |
| 運作方式 | 瀏覽器下載 HTML doc 和 JavaScript，然後由 JavaScript 渲染頁面內容。 | 伺服器生成完整的 HTML 頁面並回傳給瀏覽器，瀏覽器再進行 hydration 以增加互動性。 | 在建置時(Build time)生成靜態 HTML 頁面，並在用戶請求時直接提供這些頁面。 | 結合 SSR 和 SSG 的優點，部分頁面在建置時生成，部分頁面在運行時生成並快取。 允許在不重新構建整個網站的情況下，動態更新靜態生成的頁面。 |
| 優點 | 提升使用者體驗，避免頁面重新加載。 | 改善 SEO，初次載入速度快。 | 效能最佳，適合 SEO，適合內容不常變動的網站。 | 適合動態內容且訪問頻繁的頁面，平衡效能和即時性。 |
| 缺點 | SEO 不佳，初次載入速度較慢，尤其是 JavaScript 文件較大時。 | 需要維護伺服器，增加系統複雜度和成本 | 內容更新需要重新建置，對於大量頁面或頻繁更新的網站不適用。 | 開發難度較高，需注意快取管理。 |
| Web Vital | FMP 慢 | TTI 長、TTFB 長 |  |  |
|  |  | [https://blog.huli.tw/2023/11/27/server-side-rendering-ssr-and-isomorphic/](https://blog.huli.tw/2023/11/27/server-side-rendering-ssr-and-isomorphic/) |  |  |

個人評分表（？
![Screenshot 2024-08-25 at 11.18.51 PM](https://hackmd.io/_uploads/S1O_sTOoA.png)



### ISR 

ISR（Incremental Static Regeneration 增量靜態再生）是 Next.js 中的一個功能，允許在不重新構建整個網站的情況下，動態更新靜態生成的頁面。

這種技術結合了靜態網站生成（SSG）的性能優勢和動態內容更新的靈活性。

1. **靜態生成頁面**：
    - 當你使用 Next.js 中的 `getStaticProps` 函數時，頁面會在構建時生成靜態內容，並且可以在構建後的第一次請求中立即使用。 **→ 同 SSG 為 build time 時構建**
2. **再生成（Regeneration）**：
    - ISR 允許為靜態生成的頁面設置一個重新生成的間隔（`revalidate`），這個間隔可以是幾秒或幾分鐘不等。
    - 當用戶訪問該頁面時，若上述的 `revalidate` 已超過 ，Next.js 會在後台觸發一次新的頁面生成過程，將最新的數據靜態化，覆蓋舊的靜態文件。 **→ 這個過程並不需要重新跑一次完整的 build，也不需要重新部署整個網站。它只針對設置了 ISR 的頁面進行部分更新。**
    - 用戶不會等待頁面更新，因為舊的靜態頁面仍然會提供，直到新的頁面生成完成。  **→ 該用戶依然看到舊資料，後續的用戶才會看到新的**
        
    ![Screenshot 2024-08-25 at 11.18.56 PM](https://hackmd.io/_uploads/ry5tjpuiC.png)

        
3. **動態與靜態的平衡**：
    - ISR 讓開發者可以選擇哪些頁面應該頻繁更新（例如新聞頁面、商品價格頁面），而不需要進行整個網站的重構。
    - 這種機制適合需要經常更新內容，但又希望保留靜態網站性能優勢的應用場景。
    

除了 `revalidate` timer 呢？

### 怎麼解決首批客戶會瀏覽到過時資料的問題

**提前觸發 ISR 更新：** 通過自動化腳本或後端 API 請求來提前觸發 ISR 的再生成

**混合使用 SSR 和 ISR：** 對於某些關鍵頁面，考慮使用 SSR（服務端渲染），確保每次用戶訪問時都會獲得最新的數據。隨著頁面的穩定性增加，再切換回 ISR。這樣可以在高頻率更新的早期階段避免過時的內容。

**縮短 revalidate 間隔：** 如果預期內容可能會頻繁變動，可以縮短 ISR 的 `revalidate` 時間

**內容更新提示（UI 端）：** 在頁面上顯示提示，告知用戶內容正在更新中，並提供手動刷新按鈕以確保用戶看到最新的內容。

### On-demand ISR

[https://nextjs.org/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation](https://nextjs.org/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation)

允許開發者在特定需求時觸發靜態頁面的重新生成，而不必依賴 `revalidate` 的時間間隔。

這種方式適合需要在內容更新後立即更新頁面的場景，比如後台管理系統更新內容后，通過 API 請求觸發特定頁面的再生成。

實作方式：向特定的 API 路由發送 POST 請求，並提供一個Token和頁面路徑。

*設置 API*

```jsx
// pages/api/revalidate.js
export default async function handler(req, res) {
  // 驗證密鑰或其他安全措施
  if (req.query.secret !== 'your-secret-key') {
    return res.status(401).json({ message: 'Invalid token' });
  }

  try {
    // 重新生成指定的頁面
    await res.revalidate('/path-to-page');
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).json({ message: 'Error revalidating' });
  }
}

```

打 API

```jsx
fetch('/api/revalidate?secret=your-secret-key', {
  method: 'POST',
})
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => console.error(err));
```

之後頁面就會立即被更新
### Edge Rendering （Edge Slice Re-rendering）
→ 當時 Nextjs 還沒有完善的 middleware，也就是包含 On-demand ISR 的功能

藉由 CDN 節點的 edge server 跑 function 更新

與 ISR 同樣想解決 保持 SSG 效能 + SSR 動態內容彈性

使用 SSG build 出靜態的頁面，並把靜態的頁面放到 CDN Cache 上，當頁面有變動需求時再對 CDN edge server 發出 override 請求，並在 edge server 完成計算得到新的 props 並渲染到頁面上。

![Screenshot 2024-08-25 at 11.19.54 PM](https://hackmd.io/_uploads/ryiE26uiC.png)




### Edge Side Includes（ESI）
### 
標記語言（Markup language）

仍未被 w3c 納入 web 標準

一種伺服器端技術，允許 Web 應用程序在邊緣處進行頁面組裝和內容替換，而不必將所有動態內容從原始 Server 器端生成並發送到 Client 端。

基本範例

```html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Weather Page</title>
</head>
<body>
  <h1>Welcome to the Weather Page</h1>
  <p>Current weather:</p>
  <esi:include src="https://api.weather.com/current-weather" />
</body>
</html>

---- 寫法
<!-- Static content -->
<div class="static-content">Static content here...</div>

<!-- Dynamic content -->
<esi:include src="/dynamic/part" />

// 條件語句
<esi:choose>
  <esi:when test="$user == 'admin'">
    <!-- Admin-specific content -->
  </esi:when>
  <esi:otherwise>
    <!-- Content for regular users -->
  </esi:otherwise>
</esi:choose>

// 變數
<esi:vars>
  Hello, $user.name!
</esi:vars>
```

#### ESI 工作流程

用戶進入網站 → 向 CDN(edge server) 請求 HTML → CDN 檢查 ESI 標籤 → 打https://api.weather.com/current-weather 拿資料 → CDN 收到資料 → 插入 HTML 相對位置 → 完全處理好的 HTML → client 端

- dev tool 無法(不應該)看到 esi 語法  

CDN 端的處理，以 Cloudflare 為例的話，可以參考

[https://blog.cloudflare.com/edge-side-includes-with-cloudflare-workers](https://blog.cloudflare.com/edge-side-includes-with-cloudflare-workers)

基本上就設定 function 去決定碰到 esi 時的處理、怎麼 render 內容