@Tanstack/vue-query 的使用介绍

news2024/12/28 20:18:41

@Tanstack/vue-query 的使用介绍

前言

在今年的vue conf 会议上,提到了vue-query这个库,这里对它的基本使用做一个介绍。

会议资料地址: https://vueconf.cn/

Tanstack-query的前身是react-query,是一个本地的服务端状态管理的库,常和“乐观更新”配合使用。它会在本地维护一个基于内存的全局状态管理,当缓存中有数据的时候,优先使用缓存数据来展示,可以让用户获得非常流畅的体验(前提是网络状态正常,如果网络很差,可能长时间显示错误的数据)。同时后台请求数据,来保证数据的正确性。

tanstack-query的优势:

https://tanstack.com/query/latest/docs/framework/vue/typescript

  • 专用的devtools
  • 自动缓存请求过的数据
  • 自动后台查询
  • 窗口聚焦自动重新获取
  • 轮询/实时查询
  • 并行查询
  • 关联查询
  • 支持mutation API
  • 分页查询
  • 无限滚动
  • 请求取消
  • 自动垃圾回收
  • 错误重试
  • 数据预取
  • 支持初始化数据和占位数据
  • 自动网络监控
  • SSR 支持

简单的demo

可以从官网获取线上demo进行测试。

https://codesandbox.io/s/github/tanstack/query/tree/main/examples/vue/basic?from-embed=&file=/src/App.vue

🎯 线上demo热更新响应速度比较慢,而且不可以post更改数据。

可以使用本地启动demo项目,增加了json-server,因此可以使用restful风格的api实现增删改查的功能。

demo地址:https://github.com/hekang456/vue-query-example

如果需要使用json-server发其他请求,参考https://github.com/typicode/json-server

使用方式

这里使用最简单的代码覆盖最常见的使用场景,其他场景见文档。

1、获取数据

使用useQuery获取数据,需要设置queryKeyqueryFnqueryKey只要是可以序列化的数组即可,但需要保证全局唯一;queryFn是一个返回Promise的函数。

import { useQuery } from '@tanstack/vue-query';

const { isPending, isError, isFetching, data, error, refetch } = useQuery({
	queryKey: ['posts'],
	queryFn: fetchPostsApi
});

在这里插入图片描述

当页面打开的时候,自动请求了 ["posts"]接口,注意看 devtools 中数据的状态,从 Fetching (获取中)变成了 Stale(已过期)。

这是默认的配置,数据获取之后立刻变成“已过期”,这样当触发了一些条件的时候就会立马重新请求,比如页面聚焦、页面重新变成活跃状态等。

2、页面聚焦/页面重新变成活跃重新请求数据

这里以页面重新变成活跃为例。

在这里插入图片描述

Posts.vue页面,切换到Post.vue页面,可以看到请求了 ["post", "1"]的数据, ["post", "1"] 也是从 Fetching 变成了 Stale,而["posts"]数据状态变成了inactive(非活跃)。

当点击back回到Posts.vue页面的时候,立刻展示了缓存中的数据,同时 ["posts"]对应的接口立马重新请求后端。因为数据没有变化,所以页面也没有改变,提供了流畅的体验。

3、自动缓存

我们以第一条数据为例

在这里插入图片描述

当数据请求过之后,就会缓存起来,当再次显示时,会直接展示缓存中的数据,同时取请求后端数据。如果数据没有变化,页面也没有改变;否则修改页面。

4、修改过期时间

默认情况下,数据请求后会立刻获取,但是某些数据,开发者明确知道它很长时间都不会更新,这时候就可以设置过期时间。

这里单独修改["posts"]的过期时间.

const { isPending, isError, isFetching, data, error, refetch } = useQuery({
	queryKey: ['posts'],
	queryFn: fetchPostsApi,
  // 一小时
	staleTime: 10 * 60 * 1000,
});

在这里插入图片描述

可以看到,["posts"]数据不再会立刻过期,哪怕页面重新变成活跃状态,也只是使用缓存数据渲染,而没有请求新的数据。

5、手动刷新

调用refresh方法,来手动刷新接口,这里以["posts"]接口为例。

Posts.vue文件中,增加一个按钮,调用uesQuery返回的refresh方法。

<button @click="refetch">refresh Posts</button>

在这里插入图片描述

每次调用refresh,都会发送一个新的请求,即使数据还是Fresh状态。

6、数据轮询

增加refetchInterval属性。如果配合enable属性,可以方便的控制轮询暂停和继续。见 9。

const { isPending, isError, isFetching, data, error, refetch } = useQuery({
	queryKey: ['posts'],
	queryFn: fetchPostsApi,
	staleTime: 10 * 60 * 1000,
  // 2s轮询一次接口
	refetchInterval: 2 * 1000
});

在这里插入图片描述

这是一个非常实用的功能,避免了手写轮询带来的复杂代码逻辑。

7、自动重试

如果接口报错,默认会自动重试,但是只在get接口生效。这是为了防止其他类型的请求会多次修改后端数据。

因为前端报错的原因是多种的,比如后端处理时间太长,导致前端超时报错。但只要请求发到了后端,就会执行操作。

为了演示这种情况,需要让接口报错。我们改一下fetchPostsApi,让它报错。

export const fetchPostsApi = async (): Promise<Post[]> =>
	await fetch('http://localhost:3000/posts').then((response) => {
		// return response.json();
		throw new Error('error');
	});

在这里插入图片描述

可以看到,如果接口报错,会默认重试三次,并且重试的间隔时间翻倍。在重试过程中,数据一致处于pending状态,最后变成了error状态。

8、数据预取

假设当数据移动到某一条post数据上时,用户大概率是会点击查看的。这时候可以对这条数据预取,用户点击时可以及时显示内容。

在每一条数据上都绑定一个mouseenter事件,实现这个功能需要queryClient来实现。

需要指定唯一的queryKey,这个keyPost.vue组件中的请求方法是一样的,所以二者可以共用一份缓存数据。

<script lang="ts" setup>
import { useQuery, useQueryClient } from '@tanstack/vue-query';
import { fetchPostsApi, fetchPostApi } from './fetch';

// ......
const queryClient = useQueryClient();
const prefetchPost = async (id: number) => {
	await queryClient.prefetchQuery({
		queryKey: ['post', id],
		queryFn: () => fetchPostApi(id)
	});
};
</script>

<template>
	<h1>Posts</h1>
  // ......
	<div v-else-if="data">
		<ul>
			<li v-for="item in data" :key="item.id">
				<a
					@mouseenter="prefetchPost(item.id)"
					@click="$emit('setPostId', item.id)"
					href="#"
					:class="{ visited: isVisited(item.id) }"
					>{{ item.title }}</a
				>
			</li>
		</ul>
	</div>
</template>

在这里插入图片描述

当打开详情时,数据是瞬间展示的,并没有loading的过程。

9、控制数据获取的时机

有时候我们不希望数据立刻获取,而是等到触发了某些条件的时候在获取。

比如,我们假设["post"]中的数据,不是进入页面就需要获取,而是一些条件满足后才可以请求。以一个变量为例。

<script>
const validate = ref(false)
const { isPending, isError, isFetching, data, error, refetch } = useQuery({
	queryKey: ['posts'],
	queryFn: fetchPostsApi,
  // 只有验证通过,才可以发请求
	enabled: validate,
});
</script>

<template>
<button @click="validate = true">validate</button>
</template>

在这里插入图片描述

开始时,请求处于disabled状态,没有发送,直到点击了按钮,请求才发出。

10、关联请求

依然是用enable属性来实现的,比如官方的例子中,只有userid属性,才去发出请求。

// Get the user
const { data: user } = useQuery({
  queryKey: ['user', email],
  queryFn: () => getUserByEmail(email.value),
})

const userId = computed(() => user.value?.id)
const enabled = computed(() => !!user.value?.id)

// Then get the user's projects
const { isIdle, data: projects } = useQuery({
  queryKey: ['projects', userId],
  queryFn: () => getProjectsByUser(userId.value),
  enabled, // The query will not execute until `enabled == true`
})

11、并行请求

多个请求之间,默认是并行的,比如:

const usersQuery = useQuery({ queryKey: ['users'], queryFn: fetchUsers })
const teamsQuery = useQuery({ queryKey: ['teams'], queryFn: fetchTeams })

同时,对于同一类请求,可以使用useQueries来并行请求。

const users = computed(...)
const queries = computed(() => users.value.map(user => {
    return {
      queryKey: ['user', user.id],
      queryFn: () => fetchUserById(user.id),
    }
  })
);
const userQueries = useQueries({queries: queries})

12、分页请求

需要修改App.vue文件,把分页组件显示出来。具体代码可以看代码仓库中的写法。

<Post v-if="postId > -1" :postId="postId" @setPostId="setPostId" />
<!-- <Posts v-else :isVisited="isVisited" @setPostId="setPostId" /> -->
<PagePosts v-else />

在这里插入图片描述

可以看到,请求下一页,在请求新接口的过程中,数据依然是使用上一页的,而没有展示loading。请求成功后进行替换。

而点击上一页,会直接用缓存数据渲染,同时后台发出请求,如果数据一致,页面UI不发生变化。

这是乐观更新的一种实现。

13、修改数据

修改数据的功能比较简单,我们给列表增加一个数据。

修改App.vue文件,增加以下代码,调用useMutation

插入数据成功后,手动让["post"]数据缓存失效,这样的话,这个接口会重新发送请求。exact: true意思是精确匹配,否则数组中包含posts的所有缓存都会失效

<script lang="ts" setup>

import { addPostApi } from './fetch';
import { useMutation, useQueryClient } from '@tanstack/vue-query';

const queryClient = useQueryClient();

const { isLoading, mutate } = useMutation({
	mutationFn: addPostApi,
	onSuccess: () => {
		queryClient.invalidateQueries({ queryKey: ['posts'], exact: true });
	}
});

const addPost = () => {
	mutate({
		userId: 1,
		id: Date.now(),
		title: 'bbbbbb',
		body: 'bbbbbb'
	});
};
</script>

<template>
	<button @click="addPost">add one postId</button>
</template>

在这里插入图片描述

这里点击按钮后,发送了两个请求,第一个post请求,插入一条数据,第二个是onSuccess回调中让列表缓存失效,从而重新请求列表数据

🚀 从这里也可以看出来,App.vue文件中并没有Post.vue组件的数据,但是只要修改对应的queryKey,同样可以影响到Post.vue组件。这避免了数据逐级传递和父组件管理全部子组件数据带来的复杂度问题。

14、初始数据和占位数据

先看初始数据,initialData的数据被认为是有效的,可以存储在缓存中,为了显示效果,给它设置一个过期时间。

const { isPending, isError, isFetching, data, error, refetch } = useQuery({
	queryKey: ['posts'],
	queryFn: fetchPostsApi,
	staleTime: 1000 * 60,
	initialData: [
		{
			id: 10000,
			userId: 1,
			title: 'First Post',
			body: 'Hello World'
		}
	]
});

在这里插入图片描述

可以看到,数据不过期,就不会请求接口

再看一下占位数据的表现。

const { isPending, isError, isFetching, data, error, refetch } = useQuery({
	queryKey: ['posts'],
	queryFn: fetchPostsApi,
	staleTime: 1000 * 60,
	placeholderData: [
		{
			id: 10000,
			userId: 1,
			title: 'First Post',
			body: 'Hello World'
		}
	]
});

在这里插入图片描述

占位数据不会缓存到内存中,当真正的数据返回时,占位数据会被替换。

15、网络状态和数据状态

数据状态:

  • pending
  • success
  • error

网络状态:

  • fetching
  • paused
  • idle

🚁 数据状态和网络状态并不总是对应,数据状态对应的是缓存中的数据状态,网络状态对应的是当次网络请求。

举个例子:

1 假如["post"]接口对应的数据已经请求成功了,那么数据状态是success,网络状态是idle;如果这个请求被再次激活,数据状态依然是success,网络状态是fetching --> idle

2 假如数据状态是pending,这时候可能在网络请求过程中,网络状态是fetching,也可能网络中断,网络状态是paused

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

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

相关文章

deepin 23 下如何运行绝大数 Windows 游戏?

查看原文 最近有很多 deepiner 一直在询问&#xff1a; “deepin 真的可以玩游戏&#xff1f;” “如何在 deepin 上玩游戏&#xff1f;能玩 windows 上的游戏吗&#xff1f;” 答案是肯定的 “必须能&#xff01;”&#xff0c;下文和大家分享一下在 deepin 上玩游戏实现的…

使用IoC容器--Ninject

Ninject Ninject是一个较新的开源的IoC容器。这是简单和可扩展的。你可以从下面的位置下载IoC容器。 Ninject 或者您可以使用 NuGet 向您的项目添加Ninject。让我们从NuGet向我们的项目中添加Ninject。只需转到您的项目引用并右键单击&#xff0c;然后 ManageNuGet Packages&a…

Java基础 2. Java基础语法

Java基础 2. Java基础语法 文章目录 Java基础 2. Java基础语法2.1. 标识符2.1.1. 标识符的命名规则 :2.1.2. 标识符的命名规范: 2.2. 关键字2.3. 字面量2.3.1. Java中有哪些字面量2.3.2. 加号运算符 2.4. 变量2.5. 二进制2.6. 八进制与十六进制2.7. 原码反码补码2.7.1. byte与b…

每日OJ_牛客_字符串计数(模拟26进制)

目录 牛客_分解因数&#xff08;简单模拟&#xff09; 解析代码 牛客_分解因数&#xff08;简单模拟&#xff09; 字符串计数_美团笔试题_牛客网 解析代码 题目意思&#xff1a;按照字典序列&#xff1a;找到s1和s2之间长度在len1和len2范围内的字符串个数。直接做不好处理&…

UE4 使用AndroidGameDevelopmentExtension(AGDE)对安卓客户端做“断点调试”与“代码热更”

本文的目的 主要介绍了如何通过AndroidGameDevelopmentExtension工具、Visual Studio 2022来进行安卓包调试。 流程全过程 1、安装JDK、Gradle、Android NDK、Android SDK等环境如下&#xff0c;请自行前往官网下载&#xff0c;并确认环境参数配置正确。 2、安装 AndroidGame…

黑悟空上线即登顶,如何一路火遍全网?哪些营销方式值得关注?

几乎人人都有一个大圣梦&#xff0c;就像每个品牌也都想成为“黑神话&#xff1a;悟空”&#xff08;以下简称“黑悟空”&#xff09;。 作为国内首款3A游戏&#xff0c;黑悟空上线即登顶&#xff0c;直接刷爆全网。虽然目前热度渐歇&#xff0c;但创造的流量神话仍被津津乐道。…

大佬借助ChatGPT写论文发刊到手软,四个步骤20个顶级学术提示词指令

大家好,感谢关注。我是七哥,一个在高校里不务正业,折腾学术科研AI实操的学术人。关于使用ChatGPT等AI学术科研的相关问题可以和作者七哥(yida985)交流,多多交流,相互成就,共同进步,为大家带来最酷最有效的智能AI学术科研写作攻略。经过数月爆肝,终于完成学术AI使用教…

Mybatis【分页插件,缓存,一级缓存,二级缓存,常见缓存面试题】

文章目录 MyBatis缓存分页延迟加载和立即加载什么是立即加载&#xff1f;什么是延迟加载&#xff1f;延迟加载/懒加载的配置 缓存什么是缓存&#xff1f;缓存的术语什么是MyBatis 缓存&#xff1f;缓存的适用性缓存的分类一级缓存引入案例一级缓存的配置一级缓存的工作流程一级…

《OpenCV计算机视觉》—— 图像形态学(腐蚀、膨胀等)

文章目录 一、图像形态学基本概念二、基本运算1.简单介绍2.代码实现 三、高级运算1.简单介绍2.代码实现 一、图像形态学基本概念 图像形态学是图像处理科学的一个独立分支&#xff0c;它基于集合论和数学形态学的理论&#xff0c;专门用于分析和处理图像中的形状和结构。图像形…

分贝通助力元气森林企业支出一体化降本提效

凭借着“0糖0脂0卡”这句广告语,元气森林几乎是一锤砸中了年轻消费者的内心,让“好喝不胖”深入人心,成为了国内饮品消费的新风向标。如果我们从近两年的快消饮品中选出几款深受消费者喜爱的“国货品牌”的话,相信「元气森林」一定上榜。 元气森林成立于2016年,旗下拥有元气森林…

深入理解并实现——快排【C语言版】

目录 一、快排介绍及其思想 二、hoare版本 三、前后指针版 四、挖坑法 五、优化版本 5.1 三数取中 5.2 小区间优化 六 、非递归实现快排 七、三路划分 八、introsort 小结 一、快排介绍及其思想 快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一…

【数据结构】Map的使用与注意事项

文章目录 概念模型Map 的使用put() 和 get()getOrDefault()remove()keySet()entrySet() 注意事项 概念 Map 和 set 是一种专门用来进行搜索的容器或者数据结构&#xff0c;其搜索的效率与其具体的实例化子类有关。 以前常见的搜索方式有&#xff1a; 直接遍历&#xff0c;时间…

Spring AOP(下)原理

本文我们来学习 Spring AOP 的原理&#xff0c;也就是 Spring 是如何实现 AOP 的。Spring AOP 是基于动态代理来实现 AOP 的&#xff1b; 1. 代理模式 1.1 代理弄模式的定义 代理模式&#xff0c;也叫委托模式。 定义&#xff1a;为其他对象提供一种代理以控制这个对象的访问…

【第三版 系统集成项目管理工程师】第14章 收尾过程组

持续更新。。。。。。。。。。。。。。。 【第三版】第十四章 收尾过程组 14.1结束项目或阶段14.1.1主要输入1.项目章程-P5392.项目管理计划-P5393.项目文件-P5394.验收的可交付成果-P5405.协议-P5406.采购文档-P540 14.1.2主要输出1.最终产品、服务或成果-P5402.项目最终报告…

如何在IntelliJ IDEA中将Tab设置为4个空格

前言 IntelliJ IDEA是一个强大的开发工具&#xff0c;支持多种编程语言。为了保持代码整洁一致&#xff0c;开发者经常需要调整编辑器中的Tab和缩进设置。 步骤1: 打开设置 首先&#xff0c;启动IntelliJ IDEA。在主界面上方的菜单栏中找到 File&#xff08;文件&#xff09…

体育直播平台开发:初创公司突破资金与市场的双重挑战

在当今竞争激烈的数字娱乐行业中&#xff0c;体育直播平台的发展潜力巨大。然而&#xff0c;面对如此巨大的蓝海市场&#xff0c;对于初创公司或中小型平台而言&#xff0c;高昂的开发和运营成本可能成为进入该行业的主要障碍。本文将探讨一些可行的低成本开发策略&#xff0c;…

Matlab三维图的坐标轴标签 自动平行坐标/自动旋转

下载解压工具包&#xff1a; https://www.mathworks.com/matlabcentral/fileexchange/49542-phymhan-matlab-axis-label-alignment 添加至MATLAB路径: 在三维绘图后增加下列语句即可 ax struct(Axes, gca); align_axislabel([],ax) h3d rotate3d; set(h3d,ActionPreCa…

k8s项目的发布(金丝雀发布)

目录 三种发布方式 1.蓝绿发布 2.金丝雀发布&#xff08;灰度发布&#xff09; 实验&#xff1a;k8s实现金丝雀发布 3.滚动发布&#xff08;默认形式&#xff09; 因为应用升级以及新旧业务切换&#xff0c;所以在这个过程当中如何保证对外的服务正常是一个非常重要的问题…

/单元测试

承接上文 统一异常处理&#xff0c;封装结果-CSDN博客 ******************************************** 登录业务 Service public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {Resourceprivate JwtUtils j…

深入探索批处理中的变量与命令:从基础到高级

更多内容前往&#xff1a;孔乙己大叔 在Windows环境中&#xff0c;批处理&#xff08;Batch&#xff09;文件是一种非常有用的脚本工具&#xff0c;允许用户自动化重复性任务。通过编写批处理脚本&#xff0c;用户可以执行一系列命令&#xff0c;而无需手动输入每个命令。变量是…