使用 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 服务器