增量静态生成 ✔

静态生成 (SSG) 解决了 SSR 和 CSR 的大部分问题,但适用于渲染大部分静态内容。

当要呈现的内容是动态的或经常变化时,它会造成限制。

想想一个有多个帖子的不断增长的博客。
您不可能仅仅因为想要更正其中一篇文章中的错字而想要重建和重新部署站点。

同样,一篇新的博客文章也不应该需要对所有现有页面进行重建。

因此,单靠 SSG 不足以渲染大型网站或应用程序。

增量静态生成 (iSSG) 模式是作为 SSG 的升级引入的,以帮助解决动态数据问题并帮助静态站点针对大量频繁变化的数据进行扩展。

iSSG 允许通过在后台预渲染页面子集来更新现有页面并添加新页面,即使有新的页面请求进入也是如此。


iSSG - 示例代码

iSSG 在两个方面工作,在现有静态站点建成后逐步引入更新。

  1. 允许添加新页面

  2. 允许更新现有页面,也称为增量静态“重新”生成

添加新页面

延迟加载概念用于在构建后在网站上包含新页面。

这意味着在第一次请求时立即生成新页面。 在生成过程中,可以在前端向用户显示回退页面或加载指示器。

将此与前面针对每个产品的单个详细信息页面讨论的 SSG 方案进行比较。 404 错误页面在此处显示为不存在页面的后备。

现在让我们看看使用 iSSG 延迟加载不存在的页面所需的 Next.js 代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

// 在 getStaticPaths() 中,需要返回要在构建时预渲染的产品页面 (/products/[id]) 的 id 列表。 
// 为此,可以从数据库中获取所有产品。
export async function getStaticPaths() {
  const products = await getProductsFromDatabase();

  const paths = products.map((product) => ({
     params: { id: product.id }
  }));

  // fallback: true 意味着丢失的页面不会 404,而是可以渲染回退。
  return { paths, fallback: true };
}

// params 将包含每个生成页面的 id。
export async function getStaticProps({ params }) {
  return {
    props: {
      product: await getProductFromDatabase(params.id)
    }
  }
}

export default function Product({ product }) {
  const router = useRouter();

  if (router.isFallback) {
    return <div>Loading...</div>;
  }

  // Render product
}

在这里,我们使用了回退:true。

现在,如果与特定产品对应的页面不可用,我们会显示该页面的后备版本,例如,如上面的 Product 函数中所示的加载指示器。

同时,Next.js 会在后台生成页面。
一旦生成,它将被缓存并显示而不是回退页面。
该页面的缓存版本现在将根据请求立即显示给任何后续访问者。

对于新页面和现有页面,我们可以设置 Next.js 应该重新验证和更新它的到期时间。

这可以通过使用 revalidate 属性来实现,如下一节所示。

更新现有页面

要重新呈现现有页面,需要为页面定义合适的超时时间。
这将确保在定义的超时期限过去后重新验证页面。

超时可以设置为低至 1 秒。 用户将继续看到页面的先前版本,直到页面完成重新验证。

因此,iSSG 使用 stale-while-revalidate 策略,其中用户在重新验证时接收缓存或过时的版本。
重新验证完全在后台进行,无需完全重建。

让我们回到基于数据库中的数据为产品生成静态列表页面的示例。
为了使其服务于相对动态的产品列表,将包含设置重建页面超时的代码。

这就是包含超时后代码的样子。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

// 此函数在构建服务器上运行
export async function getStaticProps() {
  return {
    props: {
      products: await getProductsFromDatabase(),
      revalidate: 60, // 这将强制页面在 60 秒后重新验证
    }
  }
}

// 页面组件在构建时从 getStaticProps 接收 products 属性
export default function Products({ products }) {
  return (
    <>
      <h1>Products</h1>
      <ul>
        {products.map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </>
  )
}

在 60 秒后重新验证页面的代码包含在 getStaticProps() 函数中。
当请求进入可用的静态页面时,首先提供服务。

每隔一分钟,静态页面就会在后台刷新新数据。
生成后,新版本的静态文件将可用,并将在随后的一分钟内为任何新请求提供服务。

此功能在 Next.js 9.5 及更高版本中可用。


iSSG 优势

iSSG 提供了 SSG 的所有优势,以及更多优势。 以下列表详细介绍了它们。

  1. 动态数据:
    第一个优势显然是为什么要设想 iSSG。 它能够支持动态数据而无需重建站点。

  2. 速度:
    iSSG 至少与 SSG 一样快,因为数据检索和渲染仍然在后台进行。 客户端或服务器上几乎不需要处理。

  3. 可用性:
    任何页面的最新版本将始终在线可供用户访问。 即使在后台重新生成失败,旧版本仍然保持不变。

  4. 一致:
    由于每次在服务器上重新生成一页,因此数据库和后端的负载较低且性能一致。 因此,延迟没有峰值。

  5. 易于分发:
    就像 SSG 站点一样,iSSG 站点也可以通过用于提供预渲染网页的 CDN 网络进行分发。


知识点

  • getStaticPaths()
  • fallback
  • getStaticProps()
  • revalidate