# 前端效能優化 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 內容