增量静态生成如何避免首屏重复请求?

文明 Dev 阅读 25

我在用 Next.js 做一个博客,开启了 ISR(增量静态生成),但发现首屏加载时还是会发起一次 API 请求,明明页面已经预渲染了 HTML。这不就白做了吗?

我试过在 getStaticProps 里返回数据,也确认生成了静态 HTML 文件,但客户端 hydration 阶段还是会重新 fetch 数据。比如下面这段:

export async function getStaticProps() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();
  return { props: { posts }, revalidate: 60 };
}

难道 ISR 只能减少构建时间,不能真正避免首屏请求?有没有办法让首屏完全静态、不发任何请求?

我来解答 赞 1 收藏
二维码
手机扫码查看
1 条解答
玉萱的笔记
你这问题我太熟了,当年也踩过这坑。Next.js 的 ISR 确实会预渲染 HTML,但 hydration 阶段默认会重新跑一遍 getStaticProps 里的逻辑,这是它为了保证客户端和服务端数据一致做的默认行为,不是 bug 是 feature。

但真能解决,而且不复杂。关键点在于:让客户端 hydration 时知道「数据已经就绪」,别再发请求。

核心思路是用 initialProps 把服务端拉好的数据注入到页面组件里,然后在 getStaticProps 里返回 revalidate 字段的同时,确保数据结构能被 useSWR 或你自己的 fetch 逻辑识别为「已缓存」。

不过最直接的解法是:别自己再在客户端 fetch 一遍。如果你页面数据完全来自 getStaticProps,那组件里就别写 useSWRfetch 了,直接用传进来的 props.posts

但你可能有这种需求:页面初始静态渲染,后续还能自动 revalidate(比如评论数实时更新)。这时候可以这样搞:

getStaticProps 里返回数据,同时加个标记字段,比如 isStatic: true
客户端用 useSWR 时传个 initialData,让它优先用这个数据做 hydration,只在 revalidate 时才真正发请求。

示例代码:

export async function getStaticProps() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();
return {
props: {
posts,
isStatic: true,
},
revalidate: 60,
};
}

export default function BlogPage({ posts, isStatic }) {
const { data } = useSWR('/api/posts', fetcher, {
initialData: isStatic ? posts : undefined,
revalidateIfStale: false,
revalidateOnFocus: false,
});

// data 现在 hydration 时直接用的是服务端数据,不会重 fetch
}


或者更偷懒点,如果你用的是 next/linknext/router,可以配合 useSWRImmutable(需要自己封装个 hook)或者直接用 SWRConfig 包一层,给静态页加个 fallback

另外提醒一句:别用 getServerSideProps 做 ISR,那个是纯 SSR,每次请求都跑,白搭。

真要完全零请求,那就别在客户端动任何数据逻辑,纯静态页。但一般博客还是需要点动态更新的,所以用 initialData 是最稳的方案。我司现在博客站就这么干的,首屏 0 网络请求,FID 很稳。
点赞
2026-02-24 23:02