在前一篇文章中,我們暸解了如何使用 getStaticProps
讓 Next.js 可以在打包階段產生靜態的 HTML 檔案,讓使用者在瀏覽指定網站時,伺服器能夠回傳相對應的 HTML 檔案,並且因為靜態的 HTML 可以被 cache,所以有利於提升使用者看到網頁內容的速度。
此外,還了解怎麼在 dynamic routes 中處理 SSG 的問題,因為 dynamic routes 等於是可以匹配無限的 url,但是 Next.js 不可能花費無限的時間打包檔案,因此要使用 getStaticPaths
指定有哪些路徑會參與 SSG 的打包過程。
而在 getStaticPaths
我們需要回傳一個物件,物件中必須包含兩個 key,分別為 params
與 fallback
, params
定義的是 SSG 對應的路由有哪些, fallback
則是定義使用者瀏覽 params
沒有定義的頁面時的對應方式。
今天要介紹的是 Next.js 的 Incremental Static Regeneration (拿到 Google 翻譯會跑出增量靜態再生,聽起來很中二中名字 ?),這個 feature 對於有大量靜態頁面的網站非常有幫助。
先舉一個例子,在 vercel 的這篇文章中就提到電商網站這個例子,在電商網站中假設有超過 10 萬件商品,但是使用者只會搜尋他們想看的商品,並不是所有商品都會被看到,花上大量時間在打包 HTML 檔案並不符合成本。
因此,Next.js 實作了一種功能叫做 Incremental Static Regeneration,這個功能是在 next build
打包階段不用生成所有的頁面,而是讓頁面可以動態的生成,以下有兩個概念,在實作時可以取捨:
https://vercel.com/docs/next.js/incremental-static-regeneration
讀者應該已經知道 getStaticPaths
與 fallback
的組合技,便是 Incremental Static Regeneration 的實作。但是這樣會存在一個問題,儘管可以動態地打包檔案,但是電商網站的商品內容可能會快速變動,什麼時候才會重新打包呢?
根據官方的說法,在沒有設定 revalidate
的情況是不會重新生成已經存在的 HTML。
儘管,我們可以從 Next.js 官方文件或 Chrome 的 Network 中看到 SSG 預設的 cache 時間是 31536000:
Cache-Control: s-maxage=31536000, stale-while-revalidate
31536000 的單位是秒,所以換算成天的話是 365 天,但是 365 天過後,也只是重新設定 cache 與回傳同樣的 HTML 而已。
revalidate
revalidate
是 getStaticProps
的一個選擇性參數,它可以用來決定一個頁面多久會重新打包一次。
export async function getStaticProps(context) {
const res = await fetch(`https://.../data`)
const data = await res.json()
return {
props: {},
revalidate: 60,
}
}
以下是一個 revalidate
設定為 60 秒的示範圖:
https://vercel.com/docs/next.js/incremental-static-regeneration
revalidate: 60
的意思是說頁面會 60 秒後就會失去 cache 的作用,Next.js 必須為這個頁面重新打包一次,但是如果沒有使用者瀏覽這個頁面的話,Next.js 也不會主動重新打包這個頁面。
而這個設定實際上運作的方式如下:
getStaticProps
,進行重新打包的流程。在打包完成之後,Next.js 會更新 cache,並且當有使用者瀏覽該頁面時,看到的都會是新的內容。以 revalidate: 60
作為範例,Next.js 會修改 header 中的 Cache-Control
數值, s-maxage
會等於 revalidate
設定的數值。
Cache-Control: s-maxage=60, stale-while-revalidate
fallback
?要使用 Incremental Static Regeneration 這個功能, 在 getStaticPaths
中的 fallback
就必須是 true
或 'blocking'
其中一種,對於使用者來說兩個的體驗差別是有沒有 fallback page, 設定fallback: true
能夠用 isFallback
判別目前的 fallback 階段,並回傳 loading 的特效或是其他的 component 給使用者,而設定 'blocking'
則沒有 fallback page,更像是 SSR 的體驗,使用者會等待頁面生成完畢後,直接看到完整的網頁內容。
兩者看起來很相似,怎麼選擇呢?
fallback: true
v.s. fallback: 'blocking'
'blocking'
:官方推薦使用這個參數,原因雖然沒有說,但是在 Next.js 的 GitHub issue 中翻了一會兒,會發現 'blocking'
的好處是有利於 SEO,雖然對於會執行 JavaScript 的 Google 爬蟲沒有影響,但是像是 Facebook 或 Twitter 等不會執行 JavaScript 的爬蟲, 'blocking'
才能確保爬蟲拿到的資料是完整的。true
: 如上述,因為 true
會使爬蟲看到的是 fallback page,如果沒有執行 JavaScript,則無法拿到更新後的內容,如此對於 SEO 不利。但是,對於需要經過 authentication 的頁面或是後台頁面來說,也許 true
是一個好的選擇,因為不用在意 SEO,而且透過 web skeleton 可以讓使用者更快地看到網頁預載入的內容框,從另一個面向來看是可以優化 UX 的選擇。在這篇文章中,我們談到了 Next.js 的 Incremental Static Regeneration 這項重要的功能,它能夠幫助我們降低打包時間,在使用者請求頁面時才動態地生成頁面。
此外,我們還了解到如何使用 revalidate
設定重新生成頁面的時間,並且 Next.js 還會自動更新 cahce;以及了解 fallback
的 true
與 'blocking'
的優缺點,也知道使用這兩個參數的時間點。