Try   HackMD

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

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/

個人評分表(?

Screenshot 2024-08-25 at 11.18.51 PM

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

  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

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

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

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

設置 API

// 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

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

Edge Side Includes(ESI)

標記語言(Markup language)

仍未被 w3c 納入 web 標準

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

基本範例


<!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

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