NextJs 数据篇 - 数据获取 | 缓存 | Server Actions

news2025/1/23 0:44:25

NextJs 数据篇 - 数据获取 | 缓存 | Server Actions

  • 前言
  • 一. 数据获取 fetch
    • 1.1 缓存 caching
      • ① 服务端组件使用fetch
      • ② 路由处理器 GET 请求使用fetch
    • 1.2 重新验证 revalidating
      • ① 基于时间的重新验证
      • ② 按需重新验证
        • revalidatePath
        • revalidateTag
    • 1.3 缓存的退出方式
  • 二. Server Actions
    • 2.1 PageRouter下 API 简单案例
    • 2.2 AppRouter 下 Server Actions 简单案例

前言

之前讲了:

  • NextJs 初级篇 - 安装 | 路由 | 中间件
  • NextJs 渲染篇 - 什么是CSR、SSR、SSG、ISR 和服务端/客户端组件

这篇文章就打算专门讲一下NextJs中的数据获取方式、缓存以及什么是Server Actions

一. 数据获取 fetch

NextJs 中,在服务端使用 fetch 函数是常规的数据请求方式,它扩展了原生的API,在此基础上有这么几个功能(针对服务端的请求):

  • caching:缓存
  • revalidating :重新验证

例如这么一个简单的服务端组件:

export default async function Page() {
    const res = await fetch('https://jsonplaceholder.typicode.com/posts')
    const data = await res.json()

    return (
        <ul>
            {data.map((item: any) => (
                <li key={item.id}>{item.title}</li>
            ))}
        </ul>
    )
}

1.1 缓存 caching

默认情况下,NextJs 会自动缓存服务端中 fetch 请求的返回值。但是在以下情况下不会自动缓存:

  • Server Action 中使用的时候。
  • 在定义了非 GET 方法的路由处理程序中使用。

即在服务端组件内部或者只有 GET 方法的路由处理程序中使用 fetch 函数,返回结果会自动缓存。

接下来我们来测试一下缓存,为了更加直观的让大家看到缓存的生效,我们可以在next.config.mjs 文件中增加以下配置,这样在fetch的时候,就会打印相关的信息,包括缓存是否命中

/** @type {import('next').NextConfig} */
const nextConfig = {
    logging: {
        fetches: {
            fullUrl: true
        }
    }
};

export default nextConfig;

① 服务端组件使用fetch

例如我有这么一个服务端组件:

export default async function Page() {
    const res = await fetch('https://jsonplaceholder.typicode.com/posts')
    const data = await res.json()
    return (
        <ul>
            {data.map((item: any) => (
                <li key={item.id}>{item.title}</li>
            ))}
        </ul>
    )
}

页面多次访问后:可以发现缓存命中
在这里插入图片描述

② 路由处理器 GET 请求使用fetch

// app/api/getData/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
    const res = await fetch('https://jsonplaceholder.typicode.com/posts')
    const data = await res.json()
    return NextResponse.json(data)
}

然后多次访问 http://localhost:3000/api/getData,结果如下:

在这里插入图片描述

1.2 重新验证 revalidating

重新验证的定义:清除缓存数据,然后重新获取最新的数据。 而NextJs 中提供了两种方式完成重新验证:

  • 基于时间的重新验证:根据指定的时间,自动进行重新验证。
  • 按需重新验证:根据事件手动重新验证数据。

① 基于时间的重新验证

这种验证方式,只需要在fetch的时候,增加参数revalidate即可,如下代表这个请求的缓存时长为10秒钟

fetch('https://...', { next: { revalidate: 10 } })

或者在路由段配置项中进行配置,可以再页面上或者路由处理程序中增加以下属性的导出:

// layout.jsx | page.jsx | route.js
export const revalidate = 10

② 按需重新验证

我们可以在路由处理程序中或者 Server Actions 中通过两种方式来实现按需重新验证:

  • 路径 revalidatePath
  • 缓存标签 revalidateTag
revalidatePath

我们写一个简单的页面,这个页面每次加载的时候都会随机加载一个图片,但是由于fetch被缓存了,加载多次还是同一个图片。

async function getData() {
  // 接口每次调用都会返回一个随机的猫猫图片数据
  const res = await fetch('https://api.thecatapi.com/v1/images/search')
  if (!res.ok) {
    throw new Error('Failed to fetch data')
  }
 
  return res.json()
}

export default async function Page() {
  const data = await getData()
  
  return <img src={data[0].url} width="300" />
}

效果如下:
在这里插入图片描述

那我如何按需让这个fetch请求做到刷新呢?例如我创建这么一个路由处理程序:

// app/api/revalidatePathTest/route.ts
import { revalidatePath } from 'next/cache'
import { NextRequest } from 'next/server'

export async function GET(request: NextRequest) {
    const path = request.nextUrl.searchParams.get('path')

    if (path) {
        revalidatePath(path)
        return Response.json({ revalidated: true, now: Date.now() })
    }

    return Response.json({
        revalidated: false,
        now: Date.now(),
        message: 'Missing path to revalidate',
    })
}

这段代码啥意思呢?

  • 如果我请求的地址没有参数 refreshPath,那就是个普通的接口。
  • 如果我请求的地址带上参数 refreshPath,就会通过revalidatePath 函数,重新验证对应路由或者接口。

倘若我访问:http://localhost:3000/api/revalidatePathTest?refreshPath=/,之后再访问:http://localhost:3000/,可见图片发生了刷新:
在这里插入图片描述
说明成功让路由 / 进行了重新验证(缓存刷新)

revalidateTag

除此之外,NextJs中有一个路由标签系统,即revalidateTag,它的实现逻辑如下:

使用 fetch 的时候,设置一个或者多个标签标记请求
调用 revalidateTag 方法重新验证该标签对应的所有请求

例如我们修改app/page.tsx文件:

async function getData() {
  // 接口每次调用都会返回一个随机的猫猫图片数据
  const res = await fetch('https://api.thecatapi.com/v1/images/search', { next: { tags: ['refresh'] } })
  if (!res.ok) {
    throw new Error('Failed to fetch data')
  }
 
  return res.json()
}

export default async function Page() {
  const data = await getData()
  
  return <img src={data[0].url} width="300" />
}

修改revalidatePathTest

// app/api/revalidatePathTest/route.ts
import { revalidatePath } from 'next/cache'
import { NextRequest } from 'next/server'

export async function GET(request: NextRequest) {
  const tag = request.nextUrl.searchParams.get('tag')
  revalidateTag(tag)
  return Response.json({ revalidated: true, now: Date.now() })
}

操作如下:
在这里插入图片描述
我们可以发现:

  1. 当我们多次刷新 http://localhost:3000/ ,图片并没有改变,因为缓存的作用。
  2. 当我们访问:http://localhost:3000/api/revalidatePathTest?tag=refresh,我们带上了指定的tag。值为refresh
  3. 此时再次访问首页,发现图片修改。
  4. 而我们的图片在fetch的时候,await fetch('https://api.thecatapi.com/v1/images/search', { next: { tags: ['refresh'] } }),指定了tagrefresh
  5. 因此可以联动做到重新验证。

1.3 缓存的退出方式

上面说的都是重新验证,说白了就是缓存的一种刷新机制,那么我们是否有办法主动退出缓存呢?

当使用 fetch 的时候,若满足以下情形,可以做到默认退出缓存机制:

  • fetch 请求添加了 cache: 'no-store' 或者 revalidate: 0 属性。例如
fetch('', { cache: 'no-store' })
  • fetch 请求在路由处理程序中并使用了其他方法,例如POST
  • 函数体内使用到了 headerscookies 等方法。
export async function GET(request) {
  const token = request.cookies.get('token')
  return Response.json({ data: new Date().toLocaleTimeString() })
}
  • 配置了路由段选项 const dynamic = 'force-dynamic'
export const dynamic = 'force-dynamic'
  • 配置了路由段选项 fetchCache ,默认会跳过缓存

二. Server Actions

Server Actions 是指在服务端执行的异步函数但是可以在服务端和客户端组件中使用。 我们什么情况下可以用到这个呢?

  • PageRouter 情况下,若需要前后端进行交互,则需要先定义一个接口。
  • AppRouter 情况下,这种操作则可以简化为 Server Actions 。我们可以定义一个 Server Actions ,然后直接在客户端使用获取数据。

它的基本定义方式就是通过 'use server' 声明,一般分为两种:

  • 模块级别:在文件的顶部声明,那么该文件下声明的所有导出函数都是Server Actions
  • 函数级别:在函数内部的顶端添加声明,那么只有该函数是Server Actions

例如:

// 函数级别 
async function getData() {
   'use server'
   // ...
}

// 模块级别 app/serverActions/action.ts
'use server'
export async function getData() {
    // ...
}
export async function create() {
    // ...
}

2.1 PageRouter下 API 简单案例

我们定义一个接口:
在这里插入图片描述

import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
    const res = await fetch('https://jsonplaceholder.typicode.com/posts')
    const data = await res.json()
    return NextResponse.json(data)
}

然后定义一个页面,通过API的方式从后端取数据。

'use client'

import { useEffect, useState } from "react"

export default function Page() {
    const [data, setData] = useState<any>(null)
    async function getList() {
        const data = await (await fetch('/api/getData')).json();
        setData(data);
    }
    useEffect(() => {
        getList();
    }, [])
    return (
        <ul>
            {data?.map((item: any) => (
                <li key={item.id}>{item.title}</li>
            ))}
        </ul>
    )
}

2.2 AppRouter 下 Server Actions 简单案例

我们来看下使用Server Actions的简单案例,一般我们定义一个actions文件夹:
在这里插入图片描述
里面的函数如下:

'use server'
export async function getData() {
    const res = await fetch('https://jsonplaceholder.typicode.com/posts')
    const data = await res.json()
    return data;
}

然后在组件中使用:服务端组件和客户端组件都可以使用

import { getData } from '../actions/actions'

export default async function Page() {
    const data = await getData();
    return (
        <ul>
            {data.map((item: any) => (
                <li key={item.id}>{item.title}</li>
            ))}
        </ul>
    )
}

可以看到Server Actions代码更加简洁,无需手动创建一个接口。并且这个函数可以在代码的任何一个地方使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1789507.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【遂愿赠书 - 2期】:618火热来袭,网络安全书单推荐

文章目录 一、网络安全书单背景二、网络安全与编程实践书单2.1 &#x1f3f0;《内网渗透实战攻略》2.2 &#x1f6e1;️《Kali Linux高级渗透测试&#xff08;原书第4版&#xff09;》2.3 &#x1f396;️《CTF那些事儿》2.4 &#x1f680;《权限提升技术&#xff1a;攻防实战与…

MT2085 小码哥的福利

思路&#xff1a; 1. 耐受度从小到大排序&#xff0c;每一个甜品都找到第一个能忍受其甜度的手下。 例如样例&#xff1a;甜度为1的2份甜品给第1个人吃&#xff08;此人耐受为2&#xff09;&#xff1b;甜度为3的9份甜品给第2个人吃&#xff08;此人耐受为3&#xff09;&…

学习串口屏需要了解哪些方面的知识

学习串口屏需要掌握的知识主要包括以下几个方面&#xff1a; 串口通信原理&#xff1a;串口屏是基于串口通信的显示控制模组&#xff0c;因此了解串口通信的基本原理和通信协议是必要的。你需要了解串口通信的基本概念、数据格式、波特率、校验位等参数&#xff0c;以及串口通…

python嵌套列表求学生平均分

嵌套列表 3个学生&#xff0c;5门课程&#xff0c;求每门课程的平均分和最大最小分 import randomnames [张三, 李四, 王五] courses [语文, 数学, 英语, 体育, 科学] # 嵌套列表&#xff0c;scores [[74, 96, 51, 83, 77], [81, 75, 75, 77, 52], [66, 57, 56, 74, 95]] s…

在iPad上恢复丢失数据的3方法

iPad概览 iPad不仅可以用来看电影&#xff0c;还可以用来工作和学习。使用 Apple Pencil&#xff0c;您可以在 iPad 上做笔记、画画、制作音乐、编辑视频和在课堂上教学等。同时&#xff0c;由于体积小&#xff0c;您可以在商务旅行中轻松随身携带。因此&#xff0c;iPad已成为…

Spring-IoC入门案例

黑马程序员SSM框架教程 文章目录 一、创建一个项目1.1 新建项目1.2 创建dao和service包&#xff0c;项目结构如下1.3 代码1.4 运行结果 二、将service和dao交给IoC容器管理2.1 导入Spring的坐标spring-context&#xff0c;对应版本是5.2.10.RELEASE2.2 2. 配置bean2.3 获取IoC容…

使用matplotlib.animation.ArtistAnimation绘制摆线

在数学中&#xff0c;摆线&#xff08;Cycloid&#xff09;被定义为&#xff0c;一个圆在一条直线上滚动时&#xff0c;圆边界上一定点所形成的轨迹。它是一般旋轮线的一种。摆线亦称圆滚线。 摆线也是最速降线问题和等时降落问题的解。 应用matplotlib.animation.ArtistAnimat…

BUG记录——代码正确,free报错

项目场景 无具体场景&#xff0c;学习数据结构时发现的问题。 问题描述 malloc后无法释放空间&#xff0c;报错显示如下。 图中代码如下。 int main() {int* a (int*)malloc(sizeof(int));free(a);return 0; } 原因分析 我使用的是VisualStdio2022版本&#xff0c;头文件的需求…

vscode设置编辑器文件自动保存

步骤 1.打开vscode的设置 2.在搜索栏输入关键字“保存”&#xff1b; 在 Files: Auto Save 设置项&#xff0c;选择自动保存的模式

echarts 交错柱状图, 对向柱状图, 双柱状图横向

设计图样式 上代码 父组件 <bar-chart :option"energyOption"></bar-chart>initChart() {//上周能耗排行榜this.black_label ["漫张农污处理设施","邓庄农污处理设施","王桥农污处理设施","甲庄农污处理设施"…

充电宝如何选购?2024年全网充电宝选购大全!选充电宝注意这几点

在2024年的数字生活浪潮中&#xff0c;充电宝虽小&#xff0c;却是维系我们设备活力的关键。但市面上产品良莠不齐&#xff0c;安全问题频发&#xff0c;如何从中筛选出既安全又实用的充电宝&#xff0c;成为每位数码达人的必修课。别担心&#xff0c;这篇“充电宝如何选购&…

leetcode第867题:转置矩阵

matrix[i][j]需要放在转置矩阵的(j,i)位置 public class Solution {public int[][] Transpose(int[][] matrix) {int rows matrix.Length; int columns matrix[0].Length; int[][] array2 new int[columns][];// 初始化内部数组&#xff08;列数&#xff09;for (int i 0…

判断经纬度是否在某个城市内

一、从高德获取指定城市边界经纬度信息 通过apifox操作&#xff1a; 二、引入第三方jar包&#xff1a; maven地址&#xff1a;https://mvnrepository.com/ maven依赖&#xff1a; <dependency><groupId>org.locationtech.jts</groupId><artifactId>…

私域流量课程企业培训小程序网站的作用是什么

中高型企业在发展方面&#xff0c;各个环节都需俱到&#xff0c;内部培训或是外部相关课程需求度都比较高&#xff0c;比如近些年火热的私域流量运营&#xff0c;存在着不少干货输出机构或个人&#xff0c;线上线下课程培训&#xff0c;做企业培训和私域流量运营课程输出的机构…

深入分析 Android Service (完)

文章目录 深入分析 Android Service (完)1. Service 的生命周期管理2. Service 的生命周期方法2.1 onCreate()2.2 onStartCommand(Intent intent, int flags, int startId)2.3 onBind(Intent intent)2.4 onUnbind(Intent intent)2.5 onRebind(Intent intent)2.6 onDestroy() 3.…

在当前页面拿到抽屉弹窗页面中从后端返回的值 #Vue3 #两个.vue页面之间传值问题

在当前页面拿到抽屉弹窗页面中从后端返回的值 #Vue3 #两个.vue页面之间传值问题 *解决方法一&#xff1a; 将抽屉弹窗里从后端返回得到的值缓存在浏览器中&#xff0c;在当前页面中从浏览器中获取该值。 &#xff08;原理其实就是借助第三个盒子来传递一下值&#xff0c;太小学…

钣金件设计规范

(一&#xff09; 钣金 1、钣金的概念 钣金&#xff08;sheet metal&#xff09;是针对金属薄板&#xff08;厚度通常在6mm以下&#xff09;的 一种综合冷加工工艺&#xff0c;包括冲裁、折弯、拉深、成形、锻压、铆合等&#xff0c; 其显著的特征是同一零件厚度一致。 2、钣…

【代码审计】star7th/showdoc:v3.2.4 Phar反序列化写webshell

目录 漏洞速览 复现 漏洞速览 微步在线X情报社区-威胁情报查询_威胁分析平台_开放社区 主要就是两步&#xff0c;sql注入拿到用户token&#xff0c;再以用户身份上传恶意phar包打phar反序列化 因为文件上传对用户权限没有限制&#xff0c;故本文对sql注入部分不做探讨。…

Python SMTP配置示例:如何群发邮件列表?

Python SMTP配置的高级技巧&#xff1f;API接口的正确使用方法&#xff1f; Python的SMTP库为我们提供了一种简单而强大的方式来实现邮件的群发功能。Aok将通过一个示例&#xff0c;向您展示如何使用Python SMTP配置来群发邮件列表&#xff0c;并在此过程中提及AokSend邮件API…