Next.js 缓存策略的未来方向

🧑‍💻
推荐全栈学习资源:
  • Next.js 中文文档:样式和官网一样的中文文档,创造沉浸式Next.js中文学习体验。
  • 《Chrome插件全栈开发》:真实出海项目的实战教学课,讲解Chrome插件和Next.js端的全栈开发,帮助你半个月内成为全栈出海工程师。
  • 这是一个对开发者很不好的消息——Next.js 的缓存策略要在 15 版本出现 breaking change 了。说这是不好的消息,不是说新的缓存策略不好,而是说缓存作为 Next.js 基础设施之一,居然可以这样轻松变更设计,开发者如果没有注意变更文档,很容易因为升级版本掉坑里,而知道变更的开发者更可能因为修改范围太大而拒绝升级版本,也会因此错过新版本的其他特性的性能优化。

    不过,上面说的权当吐槽吧,因为该学的还得学,谁让咱们是前端,早就对这样的变化脱敏了。

    这次的 breaking change 总结来说是这样的:默认行为从“默认缓存"转变为”默认不缓存“。

    缓存策略变更后影响到三大块请求:

    • fetch 请求默认使用 'no-store'
    • GET 路由处理程序默认不缓存
    • 客户端路由默认不缓存页面组件

    接下来详细介绍变更前后的区别,阅读后你就能对升级版本需要做的事情有明确的思路了。

    fetch 请求默认使用 'no-store'

    在 Next.js 14 中,fetch 请求默认使用 'force-cache' 策略,也就是说数据会被默认缓存。

    // Next.js 14
    async function getData() {
      // 这个请求会默认被缓存
      const res = await fetch('https://api.weijunext.com/')
      return res.json()
    }
     
    export default async function Page() {
      const data = await getData()
      return <div>{data.title}</div>
    }

    在 Next.js 15 中,fetch 请求默认使用 'no-store' 策略,也就是默认不缓存。

    // Next.js 15
    async function getData() {
      // 这个请求默认不会被缓存,每次都会重新获取数据
      const res = await fetch('https://api.weijunext.com/')
      return res.json()
    }
     
    export default async function Page() {
      const data = await getData()
      return <div>{data.title}</div>
    }

    如果在 Next.js 15 中想要缓存数据,我们需要明确指定缓存参数。例如:

    1. 在单个 fetch 调用中设置缓存选项:
    async function getData() {
      const res = await fetch('https://api.weijunext.com/', { cache: 'force-cache' })
      return res.json()
    }
    1. 为路由段设置缓存选项

    如果单个路由段(即一个页面或一个布局文件)里有多个 fetch 请求,可以通过在文件顶部添加配置统一设置缓存策略:

    export const dynamic = 'force-static' // 添加配置
     
    export default async function Page() {
      const res = await fetch('https://gapis.money')
      const data = await res.json()
      return <div>{data.title}</div>
    }

    或者

    const fetchCache = 'default-cache' // 添加配置
     
    export default async function Page() {
      const res = await fetch('https://gapis.money')
      const data = await res.json()
      return <div>{data.title}</div>
    }

    添加这两种参数配置后,对应的路由段内的 fetch 请求都会由默认不缓存变成默认缓存。

    GET 路由处理程序默认不缓存

    在 Next.js 14 中,GET 路由处理程序默认会被缓存。

    // Next.js 14, app/api/data/route.js
     
    import { NextResponse } from 'next/server'
     
    export async function GET() {
      // 这个处理程序默认会被缓存
      const data = await fetchSomeData()
      return NextResponse.json({ data })
    }

    在这个示例中,除非使用了动态函数(如 cookies()、headers())或设置了动态配置选项,否则这个 GET 处理程序的响应会被缓存。

    在 Next.js 15 中,GET 路由处理程序默认不再缓存。

    // Next.js 15, app/api/data/route.js
     
    import { NextResponse } from 'next/server'
     
    export async function GET() {
      // 这个处理程序默认不会被缓存,每次请求都会执行
      const data = await fetchSomeData()
      return NextResponse.json({ data })
    }

    如果在 Next.js 15 中想要缓存 GET 路由处理程序,也可以通过添加 export const dynamic = 'force-static' 来实现。

    // Next.js 15, app/api/data/route.js
     
    import { NextResponse } from 'next/server'
     
    // 使用 'force-static' 来缓存这个路由处理程序
    export const dynamic = 'force-static'
     
    export async function GET() {
      const data = await fetchSomeData()
      return NextResponse.json({ data })
    }
    💡

    对于特殊的路由处理程序,如 sitemap.ts、opengraph-image.tsx、 icon.tsx 以及其他 Metadata 文件,在 Next.js 15 中默认行为保持不变,它们仍然默认为静态的。

    客户端路由默认不缓存页面组件

    在 Next.js 14.2.0 中,引入了实验性的 staleTimes 标志,允许自定义配置路由器缓存。默认情况下,动态路由的 staleTime 是一个非零值,默认是30,如果在 <Link> 组件里设置了 prefetch 参数,则默认是5分钟。

    // Next.js 14.2.0, next.config.js
    module.exports = {
      experimental: {
        // 默认情况下,页面组件会被缓存一段时间。
        staleTimes: {
          dynamic: 30,
          static: 180
        },
      },
    }

    在 Next.js 15 中,Page 段的 staleTime 默认为 0,这意味着在应用内导航时,客户端将始终获取最新的页面组件数据。

    // app/page.js
    export default function Page({ params }) {
      // 这个组件在每次导航时都会重新获取
      return <div>Page Content</div>
    }

    如果我们想在 Next.js 15 中保持原来的缓存行为,可以手动配置:

    // Next.js 15, next.config.js
    module.exports = {
      experimental: {
        staleTimes: {
          dynamic: 30, // 手动设置动态路由的 staleTime 为 30 秒
          static: 180
        },
      },
    }

    客户端路由不变的行为

    1. 路由切换,共享布局不会重新获取:
    // app/layout.js
    export default function Layout({ children }) {
      // 这个布局组件的数据不会在导航时重新获取
      return <div>{children}</div>
    }
    1. Loading.js 仍然自动缓存:
    // app/loading.js
    export default function Loading() {
      // 这个组件会被缓存 5 分钟或 staleTimes.static 配置的值
      return <div>Loading...</div>
    }
    1. 后退/前进导航仍然自动缓存

    结语

    从 Next.js 团队释放的信息来看,缓存策略的变更是为了提供更灵活的控制方案,而且把控制权交由开发者。我觉得这种做法是对的,但是应该在 app router 推出的时候就考虑到,而不是经过了两个大版本的更新才修正。

    不过无论如何,变化已经来了,前端圈唯一不变的就是一直变化,所以接受变化吧!

    阅读完以上内容你应该就知道升级版本要修改哪些地方了,现在 15 版本刚发布 rc 版,距离正式版发布还有一段时间,提前做好理论知识准备,等正式迁移的时候才能得心应手。

    关于我

    我是一名全栈工程师,Next.js 开源手艺人,AI降临派。

    今年致力于 Next.js 和 Node.js 领域的开源项目开发和知识分享。

    欢迎在以下平台关注我: