Nextjs 在生产中使用 App Router 缓存

本文为翻译作品,原文链接:Crazy Nextjs app router cache in production

NextJs 的缓存是一个设计用来提高网页加载速度和性能的功能。通过缓存,NextJs 能够存储一份页面的静态副本,当用户访问相同页面时,可以直接从缓存中提取,而不是每次都重新生成页面。这不仅减少了服务器的负担,也提高了用户的访问速度。NextJs 提供了几种不同的缓存策略,让开发者可以根据实际需求选择最适合的方案。

然而,对于 Next.js 新手来说,理解并有效利用其缓存系统可能会显得有些复杂和困难。缓存策略的不当使用不仅可能导致应用性能问题,还可能在开发和生产环境中产生不一致的行为,进而影响用户体验。因此,本文旨在为 Next.js 的初学者提供一个关于如何理解和利用 Next.js 缓存机制的指南,帮助你在部署生产应用之前,确保你的应用能够充分利用 Next.js 的缓存策略。

Nextjs 是一个优秀的 React SSR 解决方案,它可以让你的 Web 应用拥有更好的 SEO。从 NextJS 13 开始,新的路由模式 App Rtouer 被引入,但我发现它们在缓存系统方面的知识容易混淆,而且你可能会发现开发构建和生产构建之间的行为有所不同。因此,我相信在你将其部署到生产环境之前,理解他们的新缓存系统是至关重要的。

我想首先提醒的是,对于 NextJs,由于这个缓存系统,请确保在将其部署到生产环境之前测试生产构建。我看到很多 issue 抱怨开发构建和生产构建之间的行为不一致,例子之一:issues/52765

我个人将他们的缓存系统总结为四个不同的类别。

基于时间的控制

我们可以使用 export const revalidate = 60; 来对我们的路由进行基于时间的控制。请看以下示例。

javascript 复制代码
export default async function Home() {
  const posts = (await (
    await fetch('https://jsonplaceholder.typicode.com/posts')
  ).json()) as Post[];

  return (
    <main className={styles.main}>
      <ol>
        {posts.map((post) => (
          <li key={post.id}>
            <Link href={`/post/${post.id}`}>
              {post.id}. {post.body}
            </Link>
          </li>
        ))}
      </ol>
    </main>
  );
}

// https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
export const revalidate = 60;

在这个示例中,我们导出了 const revalidate = 60。这意味着对于 Home 路由,每60秒,它将重新生成这个页面,然后将其用作缓存。在60秒内,其他进入的请求将使用页面的缓存版本。一旦到达60秒,下一个来的请求将会触发页面的再生产过程。然后其他随后的请求将使用新生成的缓存页面。

这适用于一个页面不总是更新,但有时仍然需要更新。要记住的一点是,如果这个 revalidate 值设置为 0,它意味着总是为所有请求生成页面。当它设置为 0 时,没有缓存。

按需重新验证缓存

NextJs 还提供了一个可以重新验证路径缓存的函数 revalidatePath。假设我们有一个页面,它有一些输入字段和一个保存或提交按钮。一旦用户完成编辑并点击保存按钮,然后他们可能被重定向到一些其他页面来显示编辑后的新内容。如果你的重定向页面被缓存了,而你没有做一些处理,那么在重定向后,用户无法看到新内容,因为旧的缓存内容会显示给用户。在这种情况下,在重定向之前,我们可以调用这个 revalidatePath 函数来重新验证重定向页面。

javascript 复制代码
import { revalidatePath } from 'next/cache';

export const saveAction = () => {
  // 在这里执行一些保存动作
  
  // 然后在重定向前,我们可以重新验证新页面
  revalidatePath("/");
  redirect("/");
}

这在你想让你的大多数页面仍然被缓存以获得更好性能的同时,一旦发生某些操作,你希望页面重新生成以获取最新信息时非常有用。

使用 NextJs 定义的 fetch 进行 HTTP 调用缓存

NextJs 提供了自己的 fetch 函数来帮助处理 HTTP/API 缓存。你可以向这个函数传递一些参数属性来选择退出页面缓存。

javascript 复制代码
const getPost = async function (id: string) {
  revalidatePath(`/post/${id}`);
  return await (
    await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
      next: { revalidate: 0 }, // 看这个属性,它会选择退出缓存
    })
  ).json();
};

在 NextJs 中,我们可以传递 {next:{ revalidate: 0}} 作为第二个参数。当你传递这个参数时,NextJs 将会选择退出缓存,并总是获取数据而不是缓存返回的响应。这个 revalidate 的默认值是 false,这意味着会强制进行 HTTP 缓存。如果你不希望有这种行为,例如,你的 API 返回是动态的,那么你需要设置 {next:{ revalidate: 0}} 或使用其他 HTTP 客户端。你也可以将这个 revalidate 设置为一个小数字,让它只缓存一小段时间。

完全禁用路由缓存

有时,缓存真的让人头痛,它带来了很多问题,尤其是开发和生产之间的不同行为。那么你可能想要完全禁用缓存。我们可以设置 const revalidate = 0const dynamic = 'force-dynamic' 来实现这一点。

javascript 复制代码
export default async function Home() {
  const posts = (await (
    await fetch('https://jsonplaceholder.typicode.com/posts')
  ).json()) as Post[];

  return (
    <main className={styles.main}>
      <ol>
        {posts.map((post) => (
          <li key={post.id}>
            <Link href={`/post/${post.id}`}>
              {post.id}. {post.body}
            </Link>
          </li>
        ))}
      </ol>
    </main>
  );
}

export const revalidate = 0;
export const dynamic = 'force-dynamic';

这完全不是建议的做法。没有缓存,你可能会遇到性能问题。即使你的页面非常动态,我个人建议给它设置一个小的 revalidate 时间。

总结

我们讨论了 4 种不同的 NextJs 缓存事项,希望你现在对它有了更好的理解。再次强调,对于 NextJs,你必须在本地测试生产构建,以确保它具有你想要的行为。由于缓存系统,开发构建和生产构建可能会有不同的行为。

相关推荐
腾讯TNTWeb前端团队2 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰5 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪5 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪5 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy6 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom7 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom7 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom7 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom7 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom7 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试