Next.js 的 ISR 到底是怎么触发重新生成的?
我搞不懂 ISR 的 revalidate 机制,明明设置了 revalidate: 60,但页面好像没按预期更新。我访问页面后等了一分钟再刷新,内容还是旧的,是不是我哪里配置错了?
我的 getStaticProps 是这么写的:
export async function getStaticProps() {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return {
props: { data },
revalidate: 60,
};
}
而且我的页面是动态路由,比如 /posts/[id],是不是动态路由会影响 ISR 的行为?
首先确认你的 Next.js 版本是不是最新,旧版本可能有些 bug 影响了 ISR 的正常工作。其次,在动态路由 /posts/[id] 这种情况下,revalidate 是针对每个独立页面生效的,而不是整个路径。
还有一个容易忽略的地方,如果你在开发环境下测试 ISR,它是不会真正触发定时更新的,得部署到生产环境才能看到效果。本地调试可以试试用
next build && next start来模拟生产模式。要是还是不行,看看 API 返回的数据有没有变化,有时候后端数据没更新也会导致页面看起来没变化。另外注意下缓存设置,CDN 缓存可能会干扰 ISR 的刷新。
最后给你个小技巧,想手动触发刷新的话,可以在 getStaticProps 里加个随机查询参数,比如这样:
这个方法虽然不太优雅,但能帮你排查问题。希望这些能帮上忙,折腾 ISR 确实挺头疼的。
咱们先说清楚一个关键点:ISR 的 revalidate 不是"每隔 60 秒自动刷新一次",而是"过了 60 秒后,如果有请求来了,才触发重新生成"。
这是什么意思呢?我给你捋一下这个流程。
假设你的页面在 12:00:00 生成的,revalidate 设了 60 秒。那么在 12:01:00 之前,所有访问这个页面的请求,Next.js 都会直接返回缓存的旧页面,速度很快,因为是静态的。
关键点来了。在 12:01:00 之后,比如 12:01:30,第一个用户来访问这个页面。这时候 Next.js 会做两件事:第一,先给这个用户返回旧的缓存页面;第二,在后台悄悄触发一个重新生成的任务。注意,这个用户看到的还是旧内容!
等后台重新生成完成后,下一个用户再来访问,才能看到新内容。
所以你测试的时候,如果只是等一分钟然后刷新一次,你看到的肯定还是旧内容,因为你是那个"触发重新生成"的用户。你得再刷新一次,或者换个无痕窗口访问,才能看到更新后的页面。
原理就是这样,Next.js 用的是 stale-while-revalidate 这个策略,优先保证响应速度,后台静默更新。
再说说你的动态路由问题。
/posts/[id]这种路由,ISR 的行为取决于你的getStaticPaths怎么配置。我猜你可能配了fallback: false?如果你的
getStaticPaths是这样的:fallback: false意味着只有 paths 里列出来的页面会被预渲染,其他路径直接 404,ISR 也不会为它们生成新页面。如果你想动态路由也能用 ISR,应该改成
fallback: 'blocking'或者fallback: true:fallback: 'blocking'的好处是,当用户访问一个没有预渲染的页面时,服务器会先按需生成这个页面(SSR 方式),生成完再返回给用户,然后这个页面就会被缓存起来,后续访问就跟普通 ISR 页面一样了。fallback: true则会先返回一个"加载中"的页面状态,需要你在组件里处理 loading 状态,比较麻烦,一般不太推荐。然后还有个容易踩坑的地方,就是开发环境和生产环境行为不一样。你跑
next dev的时候,ISR 是不生效的,每次请求都会重新生成。你得next build然后next start,或者部署到 Vercel 上,ISR 才会按预期工作。最后给你一个完整的示例,你可以对照检查一下:
测试的时候记得这样操作:先访问页面,等超过 60 秒,再访问一次(这时候触发后台更新,但你看到的还是旧的),然后再访问第三次,才能看到新内容。或者你直接看服务器的控制台日志,应该能看到 revalidate 触发的重新生成请求。
如果还是不行,检查一下你有没有用自定义服务器,或者有没有配置
cache-control头之类的东西干扰了 Next.js 自己的缓存逻辑。