【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封装思路可在评论区留言,本文内容将实时更新

相关推荐
Martin -Tang4 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发5 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
老码沉思录1 小时前
写给初学者的React Native 全栈开发实战班
javascript·react native·react.js
老码沉思录1 小时前
React Native 全栈开发实战班 - 第四部分:用户界面进阶之动画效果实现
react native·react.js·ui
我不当帕鲁谁当帕鲁1 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂1 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成4 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽4 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习