【Next.js 14】网络请求 fetch(使用指南与注意点)

前言

Next.js拓展了原生的fetch,增加了缓存和重新验证机制。在服务端组件中,直接使用fetch请求接口数据。在客户端组件中,可借助第三方库请求接口数据。本文将详细阐述在Next.js中如何使用fetch获取接口数据。

对Next.js14项目搭建还不熟悉的,可先参考我的另一篇文章:给上市公司从0到1搭建Next.js14项目

1. 强缓存

对于基本不更新的网络资源走强缓存策略

在服务端组件中,使用fetch获取数据默认走强缓存策略。即只有当缓存中没有该资源或者手动清除时,才会发起网络请求。

js 复制代码
async function getProjects() {
  const res = await fetch(`https://...`)
  // 相当于 const res = fetch(`https://...`, { cache: 'force-cache' })
  const projects = await res.json()
 
  return projects
}
 
export default async function Index() {
  const projects = await getProjects()
 
  return projects.map((project) => <div>{project.name}</div>)
}

提示:在开发环境中,就算指定强缓存,接口还是会每次去重新请求。在生产环境中,缓存策略会生效,直接返回缓存的值

2. 强缓存有效时间

虽说在强缓存中,只有当缓存中没有该资源或者手动清除时,才会发起网络请求。但强缓存也是有缓存时间的,默认为365天。

对于不经常更新的网络资源可以手动设置缓存有效时间。在fetch请求中使用revalidate指定缓存时间,单位为秒

js 复制代码
//缓存有效期3600s
const revalidatedData = await fetch(`https://...`, { next: { revalidate: 60*60 } })

如果想在缓存时间内进行更新,可修改请求头,例如在请求头中添加版本信息

js 复制代码
async function getData() {
  const data = await fetch('http://127.0.0.1:9000/api/list', {
    next: { revalidate: 36000 },
    headers: {
      version: '20240406',
    },
  }).then((res) => res.json());
  return data;
}

因为 Next.js 在处理请求时,会根据请求头等信息生成唯一的缓存 key,用于缓存响应数据。如果修改请求头中自定义属性version的值,会使 Next.js 重新生成缓存信息

3. 不缓存

对于经常更新的网络资源,不走缓存策略

在fetch中使用{ cache: 'no-store' },指定每次请求都获取最新数据,不读取缓存数据

js 复制代码
const dynamicData = await fetch(`https://...`, { cache: 'no-store' })

4. 不缓存数据共享

对于缓存的请求,即使在不同子组件内多次调用,接口也只会被调用一次,不会影响性能。

对于不缓存的请求,每次发起都会实时返回最新的数据。如果在同一页面内,若两个子组件均需依赖相同的接口数据,一种处理方式是进行状态提升。

在它们共同的父级组件中发起数据请求,并通过props将数据传递至子组件。不过,这种方式可能会有两个问题:当组件层级较为复杂时,通过层层传递props会比较繁琐;若父级组件中包含静态数据,则这些数据会因为需要等待接口数据返回而延迟显示,无法立即呈现给用户。

为了解决这些问题,我们可以利用React的cache API来实现数据共享

1. 新建user/utils.ts

使用cache对接口数据进行缓存

js 复制代码
import { cache } from 'react';

//模拟网络请求
async function getNews(): Promise<{ name: number; id: number }[]> {
  return new Promise((resolve) => {
    const num = Math.random();
    setTimeout(() => {
      resolve([
        {
          name: num,
          id: 1,
        },
        {
          name: 1233,
          id: 2,
        },
      ]);
    }, 1000);
  });
}

export const getItem = cache(async () => {
  const data = await getNews();
  return data;
});

2. 新建user/layout.tsx

在布局组件中请求接口数据

js 复制代码
import { getItem } from './utils';

export default async function Layout({ children }: { children: React.ReactNode }) {
  const data = await getItem();
  return (
    <section>
      {data.map((o) => (
        <div key={o.id}>{o.name}</div>
      ))}
      ------{children}
    </section>
  );
}

3. 新建user/page.tsx

在页面组件中请求接口数据

js 复制代码
import { getItem } from './utils';

export default async function Home() {
  const data = await getItem();
  return (
    <article>
      {data.map((o) => (
        <div key={o.id}>{o.name}</div>
      ))}
    </article>
  );
}

访问路由"/user",页面显示为相同的内容,说明接口只被调用了一次。刷新页面,页面显示的内容同步刷新

5. 客户端组件中获取数据

在服务端组件中,使用fetch获取接口数据。在客户端组件中,可借助第三方库获取接口数据,例如SWR 或者 ahooks 的 useRequest

在客户端组件中获取数据,接口的请求信息将会展示在客户端,如果不想暴露请求头中的token信息,有两种解决办法:

  1. 在服务端组件中获取数据,然后通过props传递给客户端组件

  2. 通过API路由进行接口转发

对API路由不熟悉的,可参考我另一篇文章:【Next.js 14】App Router的使用(下)

6. 配置代理

在服务端组件中,数据请求在服务端,不需要考虑跨域问题。但在客户端组件中,数据请求在浏览器,需要考虑跨域问题。

假设本地开发地址为http://localhost:3000,第三方API地址为https://www.test.com/api/list,在浏览器中请求会报跨域错误。

配置代理,修改next.config.mjs

js 复制代码
const nextConfig = {
  async rewrites() {
    return [
      {
        source: '/api/:path*',
        destination: `https://www.test.com/api/:path*`,
      },
    ];
  },
};

当客户端发送以 /api 开头的请求,Next.js 会自动将其重定向到 https://www.test.com/api

提示:如果项目配置了basePath: '...',rewrites需额外增加basePath: false

js 复制代码
const nextConfig = {
  async rewrites() {
    return [
      {
        //...
        basePath: false,
      },
    ];
  },
};

7. 在动态路由中使用注意点

  1. 动态路由如果不使用generateStaticParams提前指定路由参数,接口设定的缓存策略依旧会生效,但页面的缓存策略为private, no-cache, no-store, max-age=0, must-revalidate

  2. 动态路由中的接口只要有缓存策略为不缓存,使用generateStaticParams生成的动态路由参数,打包后不会生成对应的html文件

  3. 接口数据更新后,使用generateStaticParams打包后的动态路由的html页面内容也会同步更新

8. 封装fetch请求

封装fetch请求,增加请求拦截器和响应拦截器,并可对请求接口统一管理,防止重复书写{ next: { revalidate } }等属性。

因为每个人的封装思路不同,我这里便不直接贴源码了,感兴趣的可直接访问我的源码地址:request.ts

结尾

对Next.js感兴趣的,可先关注我,后续将继续更新相关内容

如果在Next.js中,有更好的fetch封装思路可在评论区留言,本文内容将实时更新

相关推荐
慧一居士1 分钟前
flex 布局完整功能介绍和示例演示
前端
DoraBigHead3 分钟前
小哆啦解题记——两数失踪事件
前端·算法·面试
一斤代码6 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子6 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年6 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子6 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina6 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路7 小时前
React--Fiber 架构
前端·react.js·架构
coderlin_7 小时前
BI布局拖拽 (1) 深入react-gird-layout源码
android·javascript·react.js
甜瓜看代码7 小时前
1.
react.js·node.js·angular.js