19 【RTK Query】
1.目前前端常见的发起 ajax 请求的方式
- 1、使用原生的
ajax
请求 - 2、使用
jquery
封装好的ajax
请求 - 3、使用
fetch
发起请求 - 4、第三方的比如
axios
请求 - 5、
angular
中自带的HttpClient
就目前前端框架开发中来说我们在开发vue
、react
的时候一般都是使用fetch
或axios
自己封装一层来与后端数据交互,至于angular
肯定是用自带的HttpClient
请求方式,但是依然存在几个致命的弱点,
- 1、对当前请求数据不能缓存,
- 2、一个页面上由多个组件组成,但是刚好有遇到复用相同组件的时候,那么就会发起多次
ajax
请求
📢 针对同一个接口发起多次请求的解决方法,目前常见的解决方案
- 1、使用
axios
的取消发起请求,参考文档 - 2、
vue
中还没看到比较好的方法 - 3、在
rect
中可以借用类似react-query工具对请求包装一层 - 4、对于
angular
中直接使用rxjs
的操作符shareReplay
2.RTK Query 概述
RTK不仅帮助我们解决了state的问题,同时,它还为我们提供了RTK Query用来帮助我们处理数据加载的问题。RTK Query是一个强大的数据获取和缓存工具。在它的帮助下,Web应用中的加载变得十分简单,它使我们不再需要自己编写获取数据和缓存数据的逻辑。
rtk-query
是redux-toolkit
里面的一个分之,专门用来优化前端接口请求,目前也只支持在react
中使用。
RTK Query 是一个强大的数据获取和缓存工具。它旨在简化在 Web 应用程序中加载数据的常见情况,无需自己手动编写数据获取和缓存逻辑。
RTK Query 是一个包含在 Redux Toolkit 包中的可选插件,其功能构建在 Redux Toolkit 中的其他 API 之上。
Web应用中加载数据时需要处理的问题:
- 根据不同的加载状态显示不同UI组件
- 减少对相同数据重复发送请求
- 使用乐观更新,提升用户体验
- 在用户与UI交互时,管理缓存的生命周期
这些问题,RTKQ都可以帮助我们处理。首先,可以直接通过RTKQ向服务器发送请求加载数据,并且RTKQ会自动对数据进行缓存,避免重复发送不必要的请求。其次,RTKQ在发送请求时会根据请求不同的状态返回不同的值,我们可以通过这些值来监视请求发送的过程并随时中止。
我们将 createAsyncThunk
与 createSlice
一起使用,在发出请求和管理加载状态方面仍然需要进行大量手动工作。我们必须创建异步 thunk,发出实际请求,从响应中提取相关字段,添加加载状态字段,在 extraReducers
中添加处理程序以处理 pending/fulfilled/rejected
情况,并实际编写正确的状态更新。
在过去的几年里,React 社区已经意识到 “数据获取和缓存” 实际上是一组不同于 “状态管理” 的关注点。虽然你可以使用 Redux 之类的状态管理库来缓存数据,但用例差异较大,因此值得使用专门为数据获取用例构建的工具。
RTK Query 在其 API 设计中添加了独特的方法:
- 数据获取和缓存逻辑构建在 Redux Toolkit 的
createSlice
和createAsyncThunk
API 之上 - 由于 Redux Toolkit 与 UI 无关,因此 RTK Query 的功能可以与任何 UI 层一起使用
- API 请求接口是提前定义的,包括如何从参数生成查询参数和转换响应以进行缓存
- RTK Query 还可以生成封装整个数据获取过程的 React hooks ,为组件提供
data
和isFetching
字段,并在组件挂载和卸载时管理缓存数据的生命周期 - RTK Query 提供“缓存数据项生命周期函数”选项,支持在获取初始数据后通过 websocket 消息流式传输缓存更新等用例
- 我们有从 OpenAPI 和 GraphQL 模式生成 API slice 代码的早期工作示例
- 最后,RTK Query 完全用 TypeScript 编写,旨在提供出色的 TS 使用体验
📢
rtk-query
的使用环境,必须是react
版本大于 17,可以使用hooks
的版本,因为使用rtk-query
的查询都是hooks
的方式,如果你项目简单redux
都未使用到,本人不建议你用rtk-query
,可能直接使用axios
请求更加的简单方便。
3.基础开发流程
后面这些案例后端接口返回格式都是
{ "code":200, "data":[] }
- 创建一个store文件夹
- 创建一个index.ts做为主入口
- 创建一个festures/api文件夹用来装所有的API Slice
- 创建一个sudentApiSlice.js文件,并导出简单的加减方法
3.1 定义 API Slice
使用 RTK Query,管理缓存数据的逻辑被集中到每个应用程序的单个“API Slice”中。就像每个应用程序只有一个 Redux 存储一样,我们现在有一个Slice 用于 所有 我们的缓存数据。
我们将从定义一个新的 sudentApiSlice.js
文件开始。由于这不是特定于我们已经编写的任何其他“功能”,我们将添加一个新的 features/api/
文件夹并将 sudentApiSlice.js
放在那里。让我们填写 API Slice 文件,然后分解里面的代码,看看它在做什么:
features/api/sudentApiSlice.js
// 从特定于 React 的入口点导入 RTK Query 方法
import {
createApi, fetchBaseQuery } from '@reduxjs/toolkit/dist/query/react'
// 上面这个引入的会自动创建钩子
// import { createApi } from '@reduxjs/toolkit/query'
// 定义我们的单个 API Slice 对象
//createApi() 用来创建RTKQ中的API对象
// RTKQ的所有功能都需要通过该对象来进行
// createApi() 需要一个对象作为参数
export const sudentApiSlice = createApi({
reducerPath: 'studentApi', // Api的标识,不能和其他的Api或reducer重复
// 指定查询的基础信息,发送请求使用的工具
baseQuery: fetchBaseQuery({
// 我们所有的请求都有以 “/api 开头的 URL
baseUrl: 'http://localhost:8080/api',
}),
// “endpoints” 代表对该服务器的操作和请求
endpoints: builder => ({
// `getStudents` endpoint 是一个返回数据的 “Query” 操作
getStudents: builder.query({
// 请求的 URL 是“/api/all/student”
query: () => '/all/student',
}),
}),
})
// Api对象创建后,对象中会根据各种方法自动的生成对应的钩子函数
// 通过这些钩子函数,可以来向服务器发送请求
// 钩子函数的命名规则 getStudents --> useGetStudentsQuery
export const {
useGetStudentsQuery } = sudentApiSlice
上例是一个比较简单的Api对象的例子,我们来分析一下,首先我们需要调用createApi()
来创建Api对象。这个方法在RTK中存在两个版本,一个位于@reduxjs/toolkit/dist/query
下,一个位于@reduxjs/toolkit/dist/query/react
下。react目录下的版本会自动生成一个钩子,方便我们使用Api。如果不要钩子,可以引入query下的版本,当然我不建议你这么做。
createApi()
需要一个配置对象作为参数,配置对象中的属性繁多,我们暂时介绍案例中用到的属性:
reducerPath
用来设置reducer的唯一标识,主要用来在创建store时指定action的type属性,如果不指定默认为api。
baseQuery
用来设置发送请求的工具,就是你是用什么发请求,RTKQ为我们提供了fetchBaseQuery作为查询工具,它对fetch进行了简单的封装,很方便,如果你不喜欢可以改用其他工具,这里暂时不做讨论。
fetchBaseQuery
简单封装过的fetch调用后会返回一个封装后的工具函数。需要一个配置对象作为参数,baseUrl表示Api请求的基本路径,指定后请求将会以该路径为基本路径。配置对象中其他属性暂不讨论。
endpoints
Api对象封装了一类功能,比如学生的增删改查,我们会统一封装到一个对象中。一类功能中的每一个具体功能我们可以称它是一个端点。endpoints用来对请求中的端点进行配置。
endpoints是一个回调函数,可以用普通方法的形式指定,也可以用箭头函数。回调函数中会收到一个build对象,使用build对象对点进行映射。回调函数的返回值是一个对象,Api对象中的所有端点都要在该对象中进行配置。
对象中属性名就是要实现的功能名,比如获取所有学生可以命名为getStudents,根据id获取学生可以命名为getStudentById。属性值要通过build对象创建,分两种情况:
查询:build.query({})
增删改:build.mutation({})
例如:
getStudents: builder.query({
// 请求的 URL 是“/api/all/student”
query: () => '/all/student',
}),
先说query,query也需要一个配置对象作为参数。配置对象里同样有n多个属性,现在直说一个,query方法。注意不要搞混两个query,一个是build的query方法,一个是query方法配置对象中的属性,这个方法需要返回一个子路径,这个子路径将会和baseUrl拼接为一个完整的请求路径。比如:getStudets的最终请求地址是:
http://localhost:8080/api + /all/student = http://localhost:8080/api/all/student
可算是介绍完了,但是注意了这个只是最基本的配置。RTKQ功能非常强大,但是配置也比较麻烦。不过,熟了就好了。
上例中,我们创建一个Api对象studentApi,并且在对象中定义了一个getStudents方法用来查询所有的学生信息。如果我们使用react下的createApi,则其创建的Api对象中会自动生成钩子函数,钩子函数名字为useXxxQuery或useXxxMutation,中间的Xxx就是方法名,查询方法的后缀为Query,修改方法的后缀为Mutation。所以上例中,Api对象中会自动生成一个名为useGetStudentsQuery的钩子,我们可以获取并将钩子向外部暴露。
export const {
useGetStudentsQuery} = studentApi;
3.2 配置 Store
我们现在需要将 API Slice 连接到我们的 Redux 存储。我们可以修改现有的 store.js
文件,将 API slice 的 cache reducer 添加到状态中。此外,API slice 会生成需要添加到 store 的自定义 middleware。这个 middleware 必须 被添加——它管理缓存的生命周期和控制是否过期。
store/index.js
import {
configureStore } from '@reduxjs/toolkit'
import {
sudentApiSlice } from './features/api/sudentApiSlice'
// configureStore创建一个redux数据
const store = configureStore({