使用 Next.js 進行增量靜態再生 (ISR) 的完整指南

已發表: 2022-03-10
快速總結↬增量靜態再生 (ISR) 是 Jamstack 的新發展,允許您立即更新靜態內容,而無需完全重建您的網站。 Next.js 的混合方法允許您將 ISR 用於電子商務、營銷頁面、博客文章、廣告支持的媒體等。

一年前,Next.js 9.3 發布了對靜態站點生成 (SSG) 的支持,使其成為第一個混合框架。 在這一點上,我已經是一個快樂的 Next.js 用戶大約幾年了,但是這個版本使 Next.js 成為我新的默認解決方案。 在廣泛使用 Next.js 之後,我加入了 Vercel,以幫助 Tripadvisor 和華盛頓郵報等公司採用和擴展 Next.js。

在本文中,我想探索 Jamstack 的新發展:增量靜態再生 (ISR) 。 您將在下面找到 ISR 指南——包括用例、演示和權衡。

靜態站點生成的問題

Jamstack 背後的想法很吸引人:預渲染的靜態頁面可以在幾秒鐘內推送到 CDN 並在全球範圍內可用。 靜態內容速度快,對停機時間有彈性,並且可以立即被爬蟲索引。 但也有一些問題。

如果您在構建大型靜態站點時採用了 Jamstack 架構,那麼您可能會等待數小時才能構建站點。 如果將頁數加倍,則構建時間也會加倍。 讓我們考慮一下 Target.com。 每次部署都可以靜態生成數百萬個產品嗎?

構建時間圖
靜態站點生成的問題:由於構建時間與頁面數量呈線性關係,因此您可能需要等待數小時才能構建站點。 (大預覽)

即使每個頁面都是在不切實際的 1 毫秒內靜態生成的,重建整個站點仍然需要數小時。 對於大型 Web 應用程序,選擇完整的靜態站點生成是不可能的。 大型團隊需要更靈活、個性化的混合解決方案。

內容管理系統 (CMS)

對於許多團隊來說,他們網站的內容與代碼是分離的。 使用 Headless CMS 允許內容編輯者在不涉及開發人員的情況下發布更改。 但是,對於傳統的靜態站點,此過程可能會很慢。

考慮一個擁有 100,000 種產品的電子商務商店。 產品價格經常變化。 作為促銷活動的一部分,當內容編輯器將耳機的價格從 100 美元更改為 75 美元時,他們的 CMS 使用 webhook 來重建整個網站。 等待數小時以反映新價格是不可行的。

具有不必要計算的長時間構建也可能會產生額外的費用。 理想情況下,您的應用程序足夠智能,可以了解哪些產品發生了變化並逐步更新這些頁面,而無需完全重建

跳躍後更多! 繼續往下看↓

增量靜態再生 (ISR)

Next.js 允許您在構建站點創建或更新靜態頁面。 增量靜態重新生成 (ISR) 使開發人員和內容編輯者能夠在每個頁面的基礎上使用靜態生成,而無需重建整個站點。 使用 ISR,您可以在擴展到數百萬頁的同時保留靜態的優勢。

靜態頁面可以在運行時(按需)而不是在構建時使用 ISR 生成。 使用分析、A/B 測試或其他指標,您可以靈活地在構建時間上做出自己的權衡。

考慮以前擁有 100,000 種產品的電子商務商店。 靜態生成每個產品頁面的實際時間為 50 毫秒,如果沒有 ISR,這將需要將近 2 個小時。 使用 ISR,我們可以選擇:

  • 更快的構建
    在構建時生成最受歡迎的 1,000 種產品。 對其他產品的請求將是緩存未命中並按需靜態生成:1 分鐘構建。
  • 更高的緩存命中率
    在構建時生成 10,000 個產品,確保在用戶請求之前緩存更多產品:8 分鐘構建。
左邊是 Jamstack,右邊是增量靜態再生的插圖
ISR 的優勢:您可以靈活選擇在構建時或按需生成哪些頁面。 從 (A) 更快的構建或 (B) 更多的緩存中選擇。 (大預覽)

讓我們來看一個電子商務產品頁面的 ISR 示例。

入門

獲取數據

如果您以前從未使用過 Next.js,我建議您閱讀 Next.js 入門以了解基礎知識。 ISR 使用相同的 Next.js API 來生成靜態頁面: getStaticProps 。 通過指定revalidate: 60 ,我們通知 Next.js 對該頁面使用 ISR。

增量靜態再生的請求流程示意圖
增量靜態再生的請求流程示意圖。 (大預覽)
  1. Next.js 可以定義每頁的重新驗證時間。 讓我們將其設置為 60 秒。
  2. 對產品頁面的初始請求將顯示帶有原始價格的緩存頁面。
  3. 產品的數據在 CMS 中更新。
  4. 在初始請求之後和 60 秒之前對頁面的任何請求都會被緩存並且是瞬時的。
  5. 在 60 秒窗口之後,下一個請求仍將顯示緩存(陳舊)頁面。 Next.js在後台觸發頁面的重新生成。
  6. 成功生成頁面後,Next.js 將使緩存失效並顯示更新的產品頁面。 如果後台重新生成失敗,則舊頁面保持不變。
 // pages/products/[id].js export async function getStaticProps({ params }) { return { props: { product: await getProductFromDatabase(params.id) }, revalidate: 60 } }

生成路徑

Next.js 定義了在構建時生成哪些產品以及按需生成哪些產品。 讓我們通過向getStaticPaths提供前 1,000 個產品 ID 的列表,僅在構建時生成最受歡迎的 1,000 個產品。

我們需要配置 Next.js 在初始構建後請求任何其他產品時如何“回退”。 有兩個選項可供選擇: blockingtrue

  • fallback: blocking (首選)
    當對尚未生成的頁面發出請求時,Next.js 將在第一個請求時服務器渲染該頁面。 未來的請求將從緩存中提供靜態文件。
  • fallback: true
    當對尚未生成的頁面發出請求時,Next.js 將立即為第一個請求提供一個具有加載狀態的靜態頁面。 數據加載完成後,頁面將使用新數據重新渲染並被緩存。 未來的請求將從緩存中提供靜態文件。
 // pages/products/[id].js export async function getStaticPaths() { const products = await getTop1000Products() const paths = products.map((product) => ({ params: { id: product.id } })) return { paths, fallback: 'blocking' } }

權衡取捨

Next.js 首先關注最終用戶。 “最佳解決方案”是相對的,因行業、受眾和應用程序的性質而異。 通過允許開發人員在不離開框架邊界的情況下在解決方案之間切換,Next.js 讓您可以為項目選擇正確的工具。

服務器端渲染

ISR 並不總是正確的解決方案。 例如,Facebook 新聞提要不能顯示陳舊的內容。 在這種情況下,您可能希望使用 SSR 並可能使用您自己的帶有代理鍵的cache-control標頭來使內容無效。 由於 Next.js 是一個混合框架,因此您可以自己做出權衡並留在框架內。

 // You can cache SSR pages at the edge using Next.js // inside both getServerSideProps and API Routes res.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate');

SSR 和邊緣緩存類似於 ISR(尤其是在使用stale-while-revalidate緩存標頭時),主要區別在於第一個請求。 使用 ISR,如果預渲染,第一個請求可以保證是靜態的。 即使您的數據庫出現故障,或者與 API 通信出現問題,您的用戶仍然會看到正確提供的靜態頁面。 但是,SSR 將允許您根據傳入的請求自定義頁面。

注意在沒有緩存的情況下使用 SSR 會導致性能下降。 在阻止用戶查看您的網站時,每一毫秒都很重要,這會對您的 TTFB(第一個字節的時間)產生巨大影響。

靜態站點生成

ISR 對小型網站並不總是有意義。 如果您的重新驗證週期大於重建整個站點所需的時間,您不妨使用傳統的靜態站點生成。

客戶端渲染

如果你在沒有 Next.js 的情況下使用 React,那麼你使用的是客戶端渲染。 您的應用程序提供加載狀態,然後在客戶端的 JavaScript 中請求數據(例如useEffect )。 雖然這確實增加了您的託管選項(因為不需要服務器),但也需要權衡取捨。

初始 HTML 中缺少預渲染內容會導致搜索引擎優化 (SEO) 速度較慢且動態性較差。 在禁用 JavaScript 的情況下也無法使用 CSR。

ISR 後備選項

如果您的數據可以快速獲取,請考慮使用fallback: blocking 。 然後,您無需考慮加載狀態,您的頁面將始終顯示相同的結果(無論是否緩存)。 如果您的數據獲取速度很慢, fallback: true允許您立即向用戶顯示加載狀態。

ISR:不僅僅是緩存!

雖然我已經通過緩存的上下文解釋了 ISR,但它旨在在部署之間保留生成的頁面。 這意味著您可以立即回滾,而不會丟失之前生成的頁面。

每個部署都可以由一個 ID 鍵入,Next.js 使用該 ID 來持久化靜態生成的頁面。 回滾時,您可以更新密鑰以指向先前的部署,從而允許原子部署。 這意味著您可以訪問以前的不可變部署,它們將按預期工作。

  • 下面是一個使用 ISR 恢復代碼的示例:
  • 您推送代碼並獲得部署 ID 123。
  • 您的頁麵包含錯字“Smshng Magazine”。
  • 您更新 CMS 中的頁面。 無需重新部署。
  • 一旦您的頁面顯示“Smashing Magazine”,它就會保存在存儲中。
  • 您推送一些錯誤代碼並部署 ID 345。
  • 您回滾到部署 ID 123。
  • 你仍然看到“粉碎雜誌”。

恢復和持久化靜態頁面超出了 Next.js 的範圍,並且取決於您的託管服務提供商。 請注意,ISR 與使用Cache-Control標頭的服務器渲染不同,因為按照設計,緩存會過期。 它們不會跨區域共享,並且在還原時將被清除。

增量靜態再生示例

增量靜態再生適用於電子商務、營銷頁面、博客文章、廣告支持的媒體等。

  • 電子商務演示
    Next.js Commerce 是一款適用於高性能電子商務網站的一體化入門工具包。
  • GitHub 反應演示
    對最初的 GitHub 問題做出反應,並觀看 ISR 更新靜態生成的登錄頁面。
  • 靜態推文演示
    該項目在 30 秒內部署,但可以使用 ISR 按需靜態生成 5 億條推文。

立即學習 Next.js

開發人員和大型團隊選擇 Next.js 是因為它的混合方法和按需增量生成頁面的能力。 使用 ISR,您可以獲得靜態的好處和服務器渲染的靈活性。 ISR 使用next start開箱即用。

Next.js 旨在逐步採用。 使用 Next.js,您可以繼續使用現有代碼並根據需要添加盡可能多(或盡可能少)的 React。 通過從小處著手並逐步添加更多頁面,您可以通過避免完全重寫來防止功能工作脫軌。 了解更多關於 Next.js 的信息——祝大家編碼愉快!

延伸閱讀

  • Next.js 入門
  • 比較 Next.js 中的樣式方法
  • 如何使用 Next.js API 路由構建 GraphQL 服務器