iT邦幫忙

2021 iThome 鐵人賽

DAY 8
0
Modern Web

從零開始學習 Next.js系列 第 8

Day08 - 在 Next.js 中使用 pre-rendering (getStaticProps) — Part 2

前言

在前一篇文章中,我們暸解了如何使用 getStaticProps 讓 Next.js 可以在打包階段產生靜態的 HTML 檔案,讓使用者在瀏覽指定網站時,伺服器能夠回傳相對應的 HTML 檔案,並且因為靜態的 HTML 可以被 cache,所以有利於提升使用者看到網頁內容的速度。

此外,還了解怎麼在 dynamic routes 中處理 SSG 的問題,因為 dynamic routes 等於是可以匹配無限的 url,但是 Next.js 不可能花費無限的時間打包檔案,因此要使用 getStaticPaths 指定有哪些路徑會參與 SSG 的打包過程。

而在 getStaticPaths 我們需要回傳一個物件,物件中必須包含兩個 key,分別為 paramsfallbackparams 定義的是 SSG 對應的路由有哪些, fallback 則是定義使用者瀏覽 params 沒有定義的頁面時的對應方式。

Incremental Static Regeneration

今天要介紹的是 Next.js 的 Incremental Static Regeneration (拿到 Google 翻譯會跑出增量靜態再生,聽起來很中二中名字 ?),這個 feature 對於有大量靜態頁面的網站非常有幫助。

先舉一個例子,在 vercel 的這篇文章中就提到電商網站這個例子,在電商網站中假設有超過 10 萬件商品,但是使用者只會搜尋他們想看的商品,並不是所有商品都會被看到,花上大量時間在打包 HTML 檔案並不符合成本。

因此,Next.js 實作了一種功能叫做 Incremental Static Regeneration,這個功能是在 next build 打包階段不用生成所有的頁面,而是讓頁面可以動態的生成,以下有兩個概念,在實作時可以取捨:

  • Build Faster: 儘管有 10 萬件商品,根據 Google Analytics 過去的紀錄,只有 1000 件商品比較熱門,所以只要生成 1000 個產品的 HTML,藉此可以減少非常大量的打包時間。
  • Cache More: 如果想要讓商品頁面一開始就能夠被 cache,打包階段生成 10000 萬件商品的頁面,也是可行的。


https://vercel.com/docs/next.js/incremental-static-regeneration

讀者應該已經知道 getStaticPathsfallback 的組合技,便是 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

revalidategetStaticProps 的一個選擇性參數,它可以用來決定一個頁面多久會重新打包一次。

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 也不會主動重新打包這個頁面。

而這個設定實際上運作的方式如下:

  1. 60 秒以前:這時不論跟伺服器請求幾次這個頁面,都會回應同樣的 HTML 給使用者
  2. 60 秒以後:當使用者瀏覽頁面時看到的仍然是舊的內容,但是伺服器發現這個頁面已經不再被 cache,所以會重新執行 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;以及了解 fallbacktrue'blocking' 的優缺點,也知道使用這兩個參數的時間點。

Reference


上一篇
Day07 - 在 Next.js 中使用 pre-rendering (getStaticProps) — Part 1
下一篇
Day09 - 在 Next.js 中使用 pre-rendering (getServerSideProps)
系列文
從零開始學習 Next.js30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言