iT邦幫忙

2023 iThome 鐵人賽

DAY 29
0
Vue.js

Nuxt 3 實戰筆記系列 第 29

[Day 29] Nuxt 3 混合渲染 (Hybrid Rendering) 中的 SWR 與 ISR

  • 分享至 

  • xImage
  •  

前言

Nuxt 3 除了是一個 SSR 的框架外,也能允許網頁使用混合渲染 (Hybrid Rendering),混合渲染允許使用路由路徑的規則來決定不同的渲染方式、快取策略,透過這這樣子的方式,我們就可以將部分網站內容預先渲染成靜態內容或是設定快取的策略,而其他管理或登入的頁面可能不需要 SSR,我們也可以設定為單純的 SPA,如此一來,Nuxt 3 網站就具備了依據不同的路由規則來混合使用適用不同業務情境的渲染方式。

混合渲染(Hybrid Rendering)

你可以在 Nuxt 3 專案目錄下的 nuxt.config.ts 檔案中,為 Nuxt Config 添加一個 routeRules 的選項,它將可以用來設定路由的規則,包含渲染模式、快取策略、重新導向或添加標頭等等。

例如我們可以在 routeRules 建立網站中不同路由的渲染策略。

export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },
    '/admin/**': { ssr: false }
  }
})

可以透過設定 prerender: true 將網站的首頁 /,於建構時做預渲染來產生渲染好的完整頁面。

在預設情況,Nuxt 3 中的頁面會在伺服器端進行渲染,你可以透過 ssr: false 來將管理相關的頁面 /admin/** 調整為不使用 SSR 而是在客戶端進行渲染 (CSR)。

除了預渲染 (Prerender) 與禁用伺服器端渲染 (SSR) 外,渲染的策略也能支援快取與增量的靜態產生,接下來我們就來分別介紹 SWR 與 ISR。

SWR (Stale-While-Revalidate)

SWR (Stale-While-Revalidate) 是 HTTP 快取的策略,這種策略的核心就是能夠允許客戶端可以先使用快取的資料,並同時在背景中驗證快取資料是否已經過期,如果過期表示需要進行更新,就會重新的抓取資料並更新至快取中 (畫面上不會重新渲染成新的資料),而當再次有請求時,就會拿到剛剛已經更新過的快取資料。

而 SWR (Stale-While-Revalidate) 的快取策略,在伺服器端請求回應的回應標頭 (Response header) 中包含一個名為 Cache-Control 的選項,用來控制頁面的快取策略。

在 Nuxt 3 中你也可以在路由策略中,使用 SWR 來實現快取的策略,你可以透過添加 swr 選項來做控制。

export default defineNuxtConfig({
  routeRules: {
    '/articles/*': { swr: 3600 }
  }
})

以上面的設定來說,文章相關的頁面 /articles/** 會設定 SWR 快取策略,傳入數值則表示過期的時間 3600 秒。

當我們瀏覽文章頁面時,可以發現瀏覽器收到的請求回應(例如回應文字 Nuxt v1),回應標頭的 Cache-Control 如下設定:

s-maxage=3600, stale-while-revalidate

s-maxage 選項,作為回應的新鮮時間,也就是多久後才視為過期的舊資料,3600 則表示一小時內再次發送的請求將會從快取中取得資料。

s-maxage 和 max-age 其實稍微有點不大一樣,max-age 可以用來指定任何的快取的快取時間,而 s-maxage 同樣是設定快取的時間,但是只適用於共享快取,例如代理伺服器 Proxy、網站透過 Nginx 進行反向代理伺服器或 CDN 的共享快取。

Cache-Control 的 stale-while-revalidate 選項,可以傳入一個時間秒數,來驗證頁面快取的時間是否已經超過所設定的秒數,如果超過則會向伺服器發送請求來取得資料,剛好回應的資料文字變更為 Nuxt v2,就會寫入到快取中提供給未來的請求做使用。

所以當我們再次重新整理頁面,如果快取中存在頁面資料,則會依然從快取中做回應,而這一次所拿到的資料就是剛剛重新取得的新資料 Nuxt v2

{ swr: 3600 } 設定所產生的 Cache-Control: s-maxage=3600, stale-while-revalidate,表示頁面的快取時間為一小時,而因為 stale-while-revalidate 沒有傳入時間,表示除了直接回應快取中的資料 Nuxt v1 文字外,它也會同時在背景重新驗證快取的資料是否過期,如果過期就會發送請求至伺服器端請求資料,並再資料真的有變動時,更新新的資料到快取之中,不過呢,畫面上是不會重新顯示新資料的,仍維持 Nuxt v1 文字,使用者下次重新進入頁面才會再從快取中取得新資料 Nuxt v2

https://ithelp.ithome.com.tw/upload/images/20231014/20152617Dgf24j7WdZ.png

透過這樣子的方式,只有在首次沒有快取的情況下會最花費時間,使用者最明顯感覺到畫面的載入,爾後快取的驗證與重新取的資料,都是在背景做執行,使用者再次重新瀏覽網頁,就會從快取中取得資料,這樣一來畫面的載入就會非常的快速。

總結來說,SWR (Stale-While-Revalidate) 的核心概念,就是嘗試拿快取中的資料作為回應,並同時在背景中,驗證快取是否過期來重新取得最新的資料來,並將新資料更新到快取中提供給未來的請求做使用,因此使用者在瀏覽網頁時便不在需要等待伺服器端渲染 HTML 與回傳的時間。

實際上 Cache-Control 可以控制的快取策略也有許多不同的選項,如果 Nuxt 3 預設的 SWR 還不夠滿足你,那麼就需要自己來做定義回傳,更多的選項功能可以參考 Cache-Control - HTTP | MDN

ISR (Incremental Static Regeneration)

當你的網站有數以千計以上的頁面文章或產品,你可能會預先選染這些頁面,來降低使用者瀏覽頁面的等待時間,但如果你的這些頁面因為新增或變動時,你可能就要重新建構一次網站做全靜態的渲染,這顯然非常花費時間。

ISR (Incremental Static Regeneration) 是靜態產生 SSG (Static Site Generation) 技術的改良,它可以在伺服器中定時的重新產生這些被變動或新增的頁面,以確保頁面資料的新鮮程度。

舉例來說,網站中存在一個 /top100-products 的頁面,這個頁面的資料用來呈現前 100 個熱門的商品,它會隨著時間與銷量而進行變化,如果我們只對這個頁面單純的做 SSG 那麼將無法取得最新的排名狀況,而單純使用 SSR 則可能每次請求都需要等待渲染這個頁面的時間。

我們可以配置在 Nuxt Config 中的 routeRules 添加 isr 的選項來使用 ISR,讓 /top100-products 頁面可以在伺服器中定時的渲染,{ isr: 1800 } 表示每半小時重新渲染一次。

export default defineNuxtConfig({
  routeRules: {
    '/top100-products': { isr: 1800 }
  }
})

如此一來使用者下一次所取得的頁面資料,便是會隨著時間所更新的新排名,同時也保持著如靜態網頁的載入速度。

總結來說,ISR (Incremental Static Regeneration) 的核心概念,當頁面內容有調整或新增時,這些頁面不必再次的重新建構做預渲染,這些路由的頁面會在支援 ISR 的伺服器或雲端服務中定時重新渲染頁面,當使用者瀏覽頁面時就能保有靜態頁面的載入速度,又能看到定時更新後的新內容。

Nuxt 3 中 SWR 與 ISR 的小提醒

當你在 Nuxt 3 為路由頁面設定不同的渲染策略,你需要注意 SWR 與 ISR 的機制,對於快取與定時產生頁面你應該已經有一個概念,但實際上使用者在使用時,會存在一個弊端,那就是頁面雖然可以被重新驗證與取得新資料,但是這些新資料會先保存在快取之中,而這些新資料並不會立刻的顯示在畫面上,使用者需要再下一次請求時,才能從快取中取得最新的資料,這是為了確保使用者不必等待載入的時間。

例如,你希望頁面擁有如靜態頁面的載入速度,又能保持渲染新的動態資料,你選擇了使用 SWR 或 ISR,但當使用者瀏覽了這些網頁,看到的可能會是快取中的舊資料,因為首次觸發檢查就算有了新資料,也只會先更新在快取之中,只有使用者再次重新整理頁面,才會拿到上一次的新資料做渲染與再次驗證。

你所期望的「每次使用者瀏覽網頁都能看到最新資料,且擁有超快的載入速度」,會因為這個特性而有所落差,因為這個特性也可能造成一些不一致的體驗,所以你需要謹慎衡量頁面中是否適合這種渲染方式。

如果你希望頁面始終是最新的資料,你應該保持使用伺服器端渲染 SSR,來即時的渲染這些頁面。

混合渲染的配置範例

Nuxt 3 讓你可以針對不同的路由來設定不同的渲染策略,這項功能還是非常的強大,我們可以依據實際業務情況與可接受渲染特性,來進行渲染策略的調整。

例如結合不同的渲染模式,我們可以建立如下的路由渲染策略,而其他沒有配置的路由將會是由伺服器端進行渲染 (SSR)。

export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true }, // 首頁在建構打包時預渲染
    '/admin/**': { ssr: false }, // 管理相關頁面只需要在客戶端進行渲染 (CSR)
    '/articles/**': { swr: 3600 }, // 文章頁面使用 SWR 渲染模式,過期時間為 1 小時
    '/products/*': { swr: 600 }, // 產品頁面使用 SWR 渲染模式,過期時間為 10 分鐘
    '/top100-products': { isr: 1800 }, // Top 100 的產品頁面使用 ISR 渲染模式,每 30 分鐘重新渲染
    '/stats': { isr: 300 }, // 狀態頁面使用 ISR 渲染模式,每 10 分鐘重新渲染
    '/static': { isr: true } // 靜態相關頁面,只產生一次,直到下次部署才重新產生
  }
})

小結

實際上這些渲染模式最主要的差異還是在渲染的時機是在伺服器端或客戶端?、**是否要在建構打包時預渲染頁面或是結合動態產生?**這些不同的渲染策略,最終也會影響到使用者的體驗與 SEO 搜尋引擎最佳化,你可以參考網站使用體驗核心指標 (Core Web Vitals) 與根據業務情況,來配置適合的渲染規則,而有使用到需要依賴 CDN 或邊緣運算的渲染方式,也是需要謹慎評估與衡量,為了讓使用者與伺服器能有體驗與效能的平衡,這些都是需要權衡利弊的。


感謝大家的閱讀,歡迎大家給予建議與討論,也請各位大大鞭小力一些:)
如果對這個 Nuxt 3 系列感興趣,可以訂閱接收通知,也歡迎分享給喜歡或正在學習 Nuxt 3 的夥伴。

參考資料


上一篇
[Day28] Nuxt 3 建構打包與部署至 Cloudflare Workers
下一篇
[Day 30] Nuxt 3 版本的升級與套件升級的管理工具
系列文
Nuxt 3 實戰筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言