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,你必须在本地测试生产构建,以确保它具有你想要的行为。由于缓存系统,开发构建和生产构建可能会有不同的行为。

相关推荐
栈老师不回家1 小时前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙1 小时前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠1 小时前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds1 小时前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
阿伟来咯~2 小时前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱2 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai2 小时前
uniapp
前端·javascript·vue.js·uni-app
帅比九日3 小时前
【HarmonyOS Next】封装一个网络请求模块
前端·harmonyos
bysking3 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js