Version | Changes |
---|---|
v12.2.0 | On-Demand ISR is stable |
v12.1.0 | On-Demand ISR added (beta). |
v12.0.0 | Bot-aware ISR fallback added. |
v9.5.0 | Base Path added. |
Next.js 允許你在建置完畢以後添加新的頁面或更新。「增量靜態生成(Incremental Static Regeneration,ISR)」讓你不需要重新建置整個網頁就能基於原先的頁面做靜態生成,使用 ISR 能夠讓你在擴大頁面規模時依然享有靜態生成的好處。
開啟 ISR 的方式是在 getStaticProps
中新增 revalidate
這個 props:
function Blog({ posts }) { return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ) } // getStaticProps 一般只會在伺服端的建置期間被執行 // 不過只要設置 revalidate 並接收到新的請求,就可以讓它在不需要伺服器的情況下被重新執行。 export async function getStaticProps() { const res = await fetch('https://.../posts') const posts = await res.json() return { props: { posts, }, // Next.js 會試著重新生成該頁面,如果: // - 接收到請求 // - 距離上一次的請求時間超過 10(秒) revalidate: 10, } } // getStaticPaths 一般只會在伺服端的建置期間被執行 // 但如果碰到尚未生成的路徑時,他可能會在沒有伺服器的情況下被重新執行 export async function getStaticPaths() { const res = await fetch('https://.../posts') const posts = await res.json() // 根據預先渲染的 posts 來產生相對應的路徑 const paths = posts.map((post) => ({ params: { id: post.id }, })) // paths 只會在建置時間做預渲染時被生成。 // 但如果設置 { fallback: 'blocking' } 且請求的路徑不存在時將會在伺服端重新生成 return { paths, fallback: 'blocking' } } export default Blog
當對一個已經被預先渲染的頁面發出請求時,第一次會先顯示快取頁面。
當請求一個尚未生成的路徑時,第一次 Next.js 會在伺服端重新生成新的頁面。往後再碰到相同的請求時將會從快取中傳回靜態檔案。詳細可閱讀 ISR 在 Vercel 上的 全域保留快取及處理回滾(rollbacks)
注意:請確認你的上游資料供應者是否預設啟用快取機制,若是有你可能需要其停用(例如
useCdn: false
),否則重新驗證將無法抓取新的內容並更新到 ISR 快取。快取亦有可能出現在帶有Cache-Control
標頭的 CDN(被請求的端口) 上。
如果你把 revalidate
的時間設為 60
,則所有在一分鐘以前的造訪者都會看到同一版的頁面,直到一分鐘以後的某個人造訪時才會清除快取頁面。
自從 v12.2.0
發佈後 Next.js 支援對指定頁面來依照需求做靜態生成(ISR)以便手動清除快取,並讓你能夠在底下幾個情境中更方便的對網站做更新:
要使用按需重新驗證的功能,你不需要在 getStaticProps
中設置 revalidate
。如果 revalidate
被省略的話 Next.js 會套用預設值 false
(也就是不做重新驗證)並等到 revalidate()
被呼叫時時才會做重新驗證。
注意: 中間件 並不會在收到按需的 ISR 請求時被執行。因此你應該對妳想重新驗證的 對應 頁面呼叫
revalidate()
。舉例來說,如果你有一個pages/blog/[slug].js
的頁面並且把/post-1
覆寫到/blog/post-1
,那麼你應該用res.revalidate('/blog/post-1')
的方式來呼叫。
首先為你的 Next.js 應用建立只有你知道的秘密令牌(token),這是要用來避免未經授權的人存取重新驗證的 API 路徑。你可以透過底下的 URL 結構來存取該路徑(可以手動配置或搭配 webhook):
https://<your-site.com>/api/revalidate?secret=<token>
接著將令牌以 環境變數 的形式傳入到你的應用中。最後,建立重新驗證的 API 路徑:
// pages/api/revalidate.js export default async function handler(req, res) { // 透過令牌來檢查該請求是否合法 if (req.query.secret !== process.env.MY_SECRET_TOKEN) { return res.status(401).json({ message: 'Invalid token' }) } try { // 這裡應該要是完整的路徑 // 例如:"/blog/[slug]" 會對應到 "/blog/post-1"(而不是 "/post-1") await res.revalidate('/path-to-revalidate') return res.json({ revalidated: true }) } catch (err) { // 如果碰到錯誤的話 Next.js 會繼續顯示上次成功生成的頁面 return res.status(500).send('Error revalidating') } }
看我們的示範 來瞭解按需重新驗證的實際運作並提供反饋。
當你在本地運行 next dev
時 getStaticProps
會在每一次請求被觸發。如果要驗證你的按需 ISR 設置是否正確,你需要建立 生產環境建置(production build) 並開啟 生產環境伺服器(production server):
$ next build $ next start
接著你就可以檢查該靜態頁面是否有正確的被重新驗證。
當 getStaticProps
在背景運行重新生成時出現錯誤(或者是你手動拋出的錯誤),會繼續顯示上次成功生成的頁面。接著再碰到下個請求時 Next.js 會再執行一次 getStaticProps
。
export async function getStaticProps() { // 如果這個請求出現錯誤,Next.js 將廢除原本要顯示的頁面, // 並在下一個請求重新執行 getStaticProps。 const res = await fetch('https://.../posts') const posts = await res.json() if (!res.ok) { // 如果碰到伺服器發生錯誤,你可能會想拋出錯誤來避免更新快取, // 直到下次請求成功。 throw new Error(`Failed to fetch posts, received status ${res.status}`) } // 如果請求成功的話就回傳文章列表, // 以及每 10 秒重新驗證一次 return { props: { posts, }, revalidate: 10, } }
ISR 適用於 自主託管 Next.js 網站,只要運行 next start
就可以開箱即用。
你可以透過這種方式來部署到像是 Kubernetes 或 HashiCorp Nomad 這一類容器協調器(container orchestrators)。在預設情況,生成的資源會被儲存在每一個容器(pod)的記憶體中,代表每一個容器會有自己的靜態檔案副本,所以在指定的容器被請求以前有可能會顯示舊的資料。
為了確保所有容器的一致性,你可以停用記憶體快取。這樣子會讓 Next.js 伺服器只使用 ISR 在檔案系統中生成的資料。
你可以在你的 Kubernetes 容器(或其他類似的配置)中使用網路共享掛載,讓不同的容器(containers)間能夠重用相同的檔案系統快取。在使用相同掛載時 .next
資料夾(包含 next/image
的快取資料)也能被共享及重用。
要停用記憶體快取,可以在你的 next.config.js
中把 isrMemoryCacheSize
設為 0
:
module.exports = { experimental: { // 預設值為 50MB isrMemoryCacheSize: 0, }, }
注意:你必須考慮在不同容器間同時更新快取時的 競爭條件(Race condition),取決於你怎麼為共享掛載做設置。
想了解更多下一步該做什麼的資訊,我們建議閱讀下面幾個章節: