尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

RTK Query 公共 API 深度解析:基于 `@reduxjs/toolkit` API 报告的类型系统、配置项与核心函数全指南

RTK Query 公共 API 深度解析:基于 `@reduxjs/toolkit` API 报告的类型系统、配置项与核心函数全指南 RTK Query 公共 API 深度解析基于reduxjs/toolkitAPI 报告的类型系统、配置项与核心函数全指南【免费下载链接】redux-toolkitThe official, opinionated, batteries-included toolset for efficient Redux development项目地址: https://gitcode.com/gh_mirrors/re/redux-toolkit导读本文以仓库中由 API Extractor 为骨架完整剖析 Redux Toolkit 中 RTK Query 数据请求层的公共 TypeScript API 面。你将系统掌握createApi/buildCreateApi的模块化架构、CreateApiOptions的全部配置项及其默认值、BaseQueryFn基础查询契约、fetchBaseQuery/retry/setupListeners等核心工具的真实签名与实现原理并了解skipToken、fakeBaseQuery、copyWithStructuralSharing等进阶导出。读完本文你可以直接根据 API 报告解读任意版本 Redux Toolkit 的公共类型面并在实际项目中精准使用这些 API。一、这份 API 报告是什么packages/toolkit/etc/rtk-query.api.md是一份由微软 API Extractor 自动生成的API 报告文件文件头部明确声明Do not edit this file. It is a report generated by API Extractor.它的作用是锁定某个版本的公共导出面public API surface凡是标记为public的类型、函数、常量都会以规范化后的 TypeScript 签名形式被记录在这里任何对公共 API 的破坏性修改都会导致这份报告与源码不一致从而在 CI 中被发现。因此这份报告可以被视为 RTK Query 公共 API 的权威索引是版本间对比diff和迁移分析的第一手资料。报告对应包名reduxjs/toolkit其中 RTK Query 部分报告生成产物API Extractor 配置见 packages/toolkit/api-extractor.query.json报告内容与源码导出的对应关系可在 packages/toolkit/src/query/index.ts 中逐一验证需要说明的是API 报告是生成时刻的快照而源码仓库可能在其后继续演进。例如报告中的CreateApiOptions尚未包含源码中已经新增的invalidationBehavior、onSchemaFailure、catchSchemaFailure、skipSchemaValidation等选项见 createApi.tsFetchBaseQueryError在源码中还额外包含TIMEOUT_ERROR变体见 fetchBaseQuery.ts。本文以报告为主体同时结合源码给出更完整、更新的事实。二、核心入口createApi与buildCreateApi2.1createApi开箱即用的服务工厂报告中的核心声明如下export const createApi: CreateApitypeof coreModuleName其中CreateApi是一个可调用callable的泛型类型export type CreateApiModules extends ModuleName { BaseQuery extends BaseQueryFn, Definitions extends EndpointDefinitions, ReducerPath extends string api, TagTypes extends string never, ( options: CreateApiOptionsBaseQuery, Definitions, ReducerPath, TagTypes, ): ApiBaseQuery, Definitions, ReducerPath, TagTypes, Modules }注意四个类型参数都有默认值ReducerPath默认apiTagTypes默认never。这意味着你调用createApi时最终得到的Api对象会携带完整的类型信息——包括每个 endpoint 的 query arg、返回类型和 tag 类型。import { createApi, fetchBaseQuery } from reduxjs/toolkit/query const api createApi({ baseQuery: fetchBaseQuery({ baseUrl: / }), endpoints: (build) ({ getPosts: build.queryPost[], void({ query: () posts }), }), })2.2buildCreateApi模块化定制入口export function buildCreateApiModules extends [Moduleany, ...Moduleany[]]( ...modules: Modules ): CreateApiModules[number][name]这是 RTK Query 模块化架构的组装器你可以传入任意数量的模块Module构建出带有定制行为的createApi。报告中的coreModule正是默认模块export const coreModule: () ModuleCoreModule从实现上看buildCreateApi内部会依次执行每个模块的init并把injectEndpoints产生的端点定义分发给各模块见 createApi.ts。默认的createApi等价于buildCreateApi(coreModule())而 React Hooks 版本reduxjs/toolkit/query/react则是buildCreateApi(coreModule(), reactHooksModule())的产物。典型定制场景自定义 React Context示例如下const customCreateApi buildCreateApi( coreModule(), reactHooksModule({ hooks: { useDispatch: createDispatchHook(MyContext), useSelector: createSelectorHook(MyContext), useStore: createStoreHook(MyContext), }, }), )该示例直接取自源码 JSDoccreateApi.ts。模块机制依赖 TypeScript 的 declaration merging 将各模块注入的 reducer、middleware、selectors 等合并到Api类型上。三、CreateApiOptions全部配置项详解报告完整列出了CreateApiOptions的 12 个配置字段是理解 RTK Query 行为的关键export interface CreateApiOptions BaseQuery extends BaseQueryFn, Definitions extends EndpointDefinitions, ReducerPath extends string api, TagTypes extends string never, { baseQuery: BaseQuery endpoints(build: EndpointBuilderBaseQuery, TagTypes, ReducerPath): Definitions extractRehydrationInfo?: (action, { reducerPath }) CombinedState... | undefined keepUnusedDataFor?: number reducerPath?: ReducerPath refetchOnFocus?: boolean refetchOnMountOrArgChange?: boolean | number refetchOnReconnect?: boolean serializeQueryArgs?: SerializeQueryArgsunknown structuralSharing?: boolean tagTypes?: readonly TagTypes[] }各字段含义、默认值与实现依据如下表配置项类型默认值说明baseQueryBaseQuery必填无每个 endpoint 的默认请求执行器RTK Query 官方推荐fetchBaseQueryendpoints函数必填无使用build语法声明query/mutation/infiniteQuery端点reducerPathstringapi该服务挂载到 store 上的唯一 key多次createApi时必须唯一tagTypesreadonly string[][]声明 tag 类型名配合providesTags/invalidatesTags实现缓存失效keepUnusedDataFornumber秒60最后一个订阅组件卸载后缓存数据保留的时长refetchOnMountOrArgChangeboolean \| numberfalse新订阅挂载时是否重新请求传数字秒表示距上次成功超过该时长才重新请求refetchOnFocusbooleanfalse窗口重新获得焦点时是否重新请求全部已订阅查询需setupListenersrefetchOnReconnectbooleanfalse网络重新连接时是否重新请求需setupListenersserializeQueryArgs函数defaultSerializeQueryArgs自定义查询缓存 key 的生成逻辑structuralSharingbooleantrue响应数据是否做结构化共享避免不必要的引用变化extractRehydrationInfo函数无从任意 action 中提取缓存状态用于 SSR/水合如配合 next-redux-wrapper 的HYDRATE3.1 默认值在源码中的落地buildCreateApi的实现中有一段带默认值的选项合并逻辑createApi.tsconst optionsWithDefaults: CreateApiOptionsany, any, any, any { reducerPath: api, keepUnusedDataFor: 60, refetchOnMountOrArgChange: false, refetchOnFocus: false, refetchOnReconnect: false, invalidationBehavior: delayed, ...options, ... }源码确认了报告的默认值描述keepUnusedDataFor为 60 秒、三个refetch*均为false、reducerPath为api。同时源码还展示了serializeQueryArgs的优先级逻辑endpoint 级serializeQueryArgs 全局serializeQueryArgsdefaultSerializeQueryArgs并且当用户自定义函数返回对象/原始值时仍会交给defaultSerializeQueryArgs再做一次序列化。3.2serializeQueryArgs与缓存 key默认序列化实现见 defaultSerializeQueryArgs.ts它会将 query args 用JSON.stringify序列化自动处理bigint并对对象 key 排序后拼接为${endpointName}(${serialized})形式保证useQuery({ a: 1, b: 2 })与useQuery({ b: 2, a: 1 })命中同一个缓存条目。该模块还使用WeakMap对纯对象做序列化结果缓存避免重复计算。3.3extractRehydrationInfo的典型用法报告给出了完整的签名源码 JSDoc 中提供了 next-redux-wrapper 的水合示例createApi.tsfunction isHydrateAction(action: Action): action is PayloadActionRootState { return action.type HYDRATE } export const api createApi({ baseQuery: fetchBaseQuery({ baseUrl: / }), extractRehydrationInfo(action, { reducerPath }): any { if (isHydrateAction(action)) { return action.payload[reducerPath] } }, endpoints: (build) ({ /* omitted */ }), })实现中该回调会被weakMapMemoize包装并缓存且hasRehydrationInfo也会复用该结果createApi.ts。四、类型系统Api、ApiModules与Module4.1Api模块合并后的最终形态export type Api BaseQuery extends BaseQueryFn, Definitions extends EndpointDefinitions, ReducerPath extends string, TagTypes extends string, Enhancers extends ModuleName CoreModule, UnionToIntersection ApiModulesBaseQuery, Definitions, ReducerPath, TagTypes[Enhancers] { injectEndpointsNewDefinitions extends EndpointDefinitions(_: { endpoints: (build: EndpointBuilderBaseQuery, TagTypes, ReducerPath) NewDefinitions overrideExisting?: boolean }): ApiBaseQuery, Definitions NewDefinitions, ReducerPath, TagTypes, Enhancers enhanceEndpointsNewTagTypes extends string never(_: { addTagTypes?: readonly NewTagTypes[] endpoints?: ReplaceTagTypes... extends infer NewDefinitions ? { [K in keyof NewDefinitions]?: PartialNewDefinitions[K] | ((definition: NewDefinitions[K]) void) } : never }): Api... }关键点UnionToIntersection将多个模块Enhancers索引到的联合类型交叉合并成一个完整类型从而让api.reducer、api.middleware、api.endpoints.*等来自不同模块的成员同时存在。injectEndpoints用于代码分割场景向已有 API 注入新端点并返回携带这些新端点类型的新Api。其endpoints参数同样接收build回调。enhanceEndpoints用于代码生成场景可追加 tag 类型addTagTypes并通过Partial部分覆盖或通过函数形式就地修改已有端点定义。实现细节createApi.ts中值得注意两点injectEndpoints内部会把build.query / mutation / infiniteQuery包装的配置打上type标记ENDPOINT_QUERY等再写入context.endpointDefinitions并分发给每个已初始化模块。overrideExisting支持三态true覆盖、false/缺省不覆盖并在开发环境打印console.error警告、throw直接抛错。这一throw取值是源码中比报告更新的扩展apiTypes.ts。4.2ApiModules与Module插拔式架构export interface ApiModules BaseQuery extends BaseQueryFn, Definitions extends EndpointDefinitions, ReducerPath extends string, TagTypes extends string, {} export type ModuleName extends ModuleName { name: Name init...(api, options, context): { injectEndpoint(endpointName: string, definition: EndpointDefinitionany, any, any, any): void } }ApiModules在报告中是一个空壳接口真正的内容通过declaration merging由各模块填充——例如coreModule注入 reducer、middleware、selector 生成器见 core/module.ts。Module.init负责在 API 初始化时注册模块逻辑并返回injectEndpoint以接收后续injectEndpoints注入的新端点。ModuleName定义为keyof ApiModulesany, any, any, any即所有已声明模块的名字的并集。五、BaseQueryFn基础查询契约报告给出了整个 RTK Query 请求层的核心契约类型export type BaseQueryFn Args any, Result unknown, Error unknown, DefinitionExtraOptions { copyWithStructuralSharing?: boolean }, Meta {}, ( args: Args, api: BaseQueryApi, extraOptions: DefinitionExtraOptions, ) MaybePromiseQueryReturnValueResult, Error, Meta含义一个 baseQuery 是一个接收(args, api, extraOptions)三个参数的函数返回或 Promise 化一个QueryReturnValue。QueryReturnValue是一个判别联合baseQueryTypes.ts要么是{ error, meta? }要么是{ data, meta? }二者互斥。baseQuery 永远不应该抛异常而是返回error字段。BaseQueryApi提供signal、abort、dispatch、getState、extra、endpoint、type等上下文query 场景下还包含forced与queryCacheKeybaseQueryTypes.ts。配套的工具类型BaseQueryEnhancer定义了包装 baseQuery 的增强器export type BaseQueryEnhancer AdditionalArgs unknown, AdditionalDefinitionExtraOptions unknown, Config void, BaseQuery extends BaseQueryFn( baseQuery: BaseQuery, config: Config, ) BaseQueryFn BaseQueryArgBaseQuery AdditionalArgs, BaseQueryResultBaseQuery, BaseQueryErrorBaseQuery, BaseQueryExtraOptionsBaseQuery AdditionalDefinitionExtraOptions 它泛型地接收任意BaseQueryFn追加参数与 extraOptions 后返回一个新的 baseQuery。retry就是该类型的实例。六、fetchBaseQueryfetch 的轻量封装6.1 函数签名与配置参数export function fetchBaseQuery({ baseUrl, prepareHeaders, fetchFn, paramsSerializer, ...baseFetchOptions }?: FetchBaseQueryArgs): BaseQueryFn string | FetchArgs, unknown, FetchBaseQueryError, {}, FetchBaseQueryMeta FetchBaseQueryArgs的完整字段含源码中的新增项见 fetchBaseQuery.ts参数类型默认值说明baseUrlstring无API 服务的基准 URL会与 endpoint 的url拼接prepareHeaders(headers, api) Headers \| void恒等函数注入认证等请求头可访问getState、arg、endpoint、type、forcedfetchFn(input, init?) PromiseResponse全局fetch自定义 fetch 实现适用于 SSR如 cross-fetchparamsSerializer(params) stringURLSearchParams自定义 query 字符串序列化isJsonContentType(headers) boolean匹配application/json与application/vnd.apijson判断响应/请求体是否按 JSON 处理jsonContentTypestringapplication/json自动设置 content-type 时使用的值jsonReplacer(key, value) any无传给JSON.stringify的 replacerresponseHandlercontent-type \| json \| text \| 函数json响应解析策略validateStatus(response, body) booleanstatus 200 status 299判断响应是否成功timeoutnumber毫秒无请求超时时间6.2FetchArgs与FetchBaseQueryMetaexport interface FetchArgs extends CustomRequestInit { body?: any params?: Recordstring, any responseHandler?: ResponseHandler url: string validateStatus?: (response: Response, body: any) boolean }FetchArgs是fetchBaseQuery可接受的两种参数之一另一种是纯字符串 URL。params会被自动拼接到 URL 上responseHandler与validateStatus可针对单次请求覆盖全局设置源码中还可传timeout毫秒。export type FetchBaseQueryMeta { request: Request response?: Response }FetchBaseQueryMeta暴露了底层Request与Response的克隆体可用于在queryFulfilled.meta、onQueryStarted等生命周期中读取响应头等信息。6.3FetchBaseQueryError的错误变体报告中的判别联合export type FetchBaseQueryError | { status: number; data: unknown } // HTTP 状态码错误 | { status: FETCH_ERROR; data?: undefined; error: string } // fetch 本身执行失败 | { status: PARSING_ERROR; originalStatus: number; data: string; error: string } // 解析失败 | { status: CUSTOM_ERROR; data?: unknown; error: string } // queryFn 自定义错误源码还增加了第五个变体{ status: TIMEOUT_ERROR; ... }请求超时fetchBaseQuery.ts。错误处理流程见 error-handling.mdx 文档。6.4 实现要点从实现看fetchBaseQuery.ts几个关键行为信号合并当设置timeout时使用anySignal(api.signal, timeoutSignal(timeout))将外部 AbortSignal 与超时信号合并超时会产生TimeoutError。JSON 智能序列化仅当 body 是可 JSON 化对象纯对象、数组或带toJSON时才自动JSON.stringify并设置content-type对FormData、Blob等则删除 content-type 交给浏览器自动处理。响应流双读由于 node-fetch 的流限制实现会同时消费响应与克隆体的流确保解析正常。错误归一化fetch抛错时根据错误名返回FETCH_ERROR或TIMEOUT_ERROR解析失败返回PARSING_ERROR携带原始文本data与originalStatusvalidateStatus不通过时返回{ status, data }。七、端点定义类型QueryDefinition、MutationDefinition与EndpointDefinitionexport type QueryDefinition QueryArg, BaseQuery extends BaseQueryFn, TagTypes extends string, ResultType, ReducerPath extends string string, BaseEndpointDefinitionQueryArg, BaseQuery, ResultType QueryExtraOptionsTagTypes, ResultType, QueryArg, BaseQuery, ReducerPath export type MutationDefinition... BaseEndpointDefinition... MutationExtraOptions... export type EndpointDefinition... | QueryDefinitionQueryArg, BaseQuery, TagTypes, ResultType, ReducerPath | MutationDefinitionQueryArg, BaseQuery, TagTypes, ResultType, ReducerPath export type EndpointDefinitions Recordstring, EndpointDefinitionany, any, any, anyQueryDefinition/MutationDefinition由公共的BaseEndpointDefinition携带type标记、query/queryFn、providesTags/invalidatesTags、keepUnusedDataFor、structuralSharing等与各自的扩展选项交叉组成。EndpointDefinitions就是端点名 → 定义的映射表。从 endpointDefinitions.ts 的源码看还有第三种端点类型infiniteQueryENDPOINT_INFINITEQUERY对应InfiniteQueryDefinition用于无限滚动场景build对象同时提供query、mutation、infiniteQuery三个构造器见 createApi.ts。QueryStatus枚举export enum QueryStatus { fulfilled fulfilled, pending pending, rejected rejected, uninitialized uninitialized, }定义于 core/apiState.ts。四种状态覆盖了查询从未发起到完成/失败的完整生命周期React Hooks 返回的status字段取值即来自该枚举。八、retry指数退避重试增强器export const retry: BaseQueryEnhancer unknown, RetryOptions, void | RetryOptions { fail: typeof fail_2 }retry是一个BaseQueryEnhancer同时还挂载了fail方法。RetryOptionsretry.tsexport type RetryOptions { backoff?: (attempt, maxRetries, signal?) Promisevoid } ( | { maxRetries?: number; retryCondition?: undefined } // 按次数重试默认 5 次 | { retryCondition?: RetryConditionFunction; maxRetries?: undefined } // 按条件重试 )默认退避策略是指数退避 随机抖动第 n 次重试前等待(Math.random() 0.4) * (300 attempt)毫秒即约 600ms、1200ms、2400ms……见 retry.ts。maxRetries默认5且可被 endpoint 级extraOptions覆盖源码通过数组取末值决定最终生效值。retry.fail(error, meta)会抛出一个带throwImmediately标记的HandledError用于立即终止重试例如 401 鉴权失败不需要重试。该机制在retryWithBackoff的 catch 分支中通过e.throwImmediately判断retry.ts。典型用法来自源码 JSDocimport { createApi, fetchBaseQuery, retry } from reduxjs/toolkit/query/react const staggeredBaseQuery retry(fetchBaseQuery({ baseUrl: / }), { maxRetries: 5 }) export const api createApi({ baseQuery: staggeredBaseQuery, endpoints: (build) ({ getPosts: build.queryPostsResponse, void({ query: () ({ url: posts }) }), getPost: build.queryPostsResponse, string({ query: (id) ({ url: post/${id} }), extraOptions: { maxRetries: 8 }, // 按端点覆盖重试次数 }), }), })九、setupListeners启用焦点/网络重取export function setupListeners( dispatch: ThunkDispatchany, any, any, customHandler?: ( dispatch, actions: { onFocus; onFocusLost; onOnline; onOffline }, ) () void, ): () voidsetupListeners是让refetchOnFocus与refetchOnReconnect生效的前提报告中CreateApiOptions的两个相关字段也明确标注了这一点。默认实现core/setupListeners.ts会在window上监听四个事件事件触发动作focusdispatchonFocusactionvisibilitychange页面变为可见时 dispatchonFocus否则onFocusLostonlinedispatchonOnlineactionofflinedispatchonOfflineaction返回值为取消订阅函数调用后移除全部监听。customHandler允许完全自定义监听逻辑。四个 action creatoronFocus、onFocusLost、onOnline、onOffline均以__rtkq/为前缀INTERNAL_PREFIX创建。最简用法setupListeners(store.dispatch)十、skipToken与已废弃的skipSelectorexport const skipToken: unique symbol export type SkipToken typeof skipToken // public deprecated export const skipSelector: symbolskipToken是唯一 symbol作为 query 参数传入即可让该查询跳过执行同时保持类型安全例如参数尚未就绪时不发请求const id maybeId ?? skipToken const { data } useGetPostQuery(id) // id 为 skipToken 时不发起请求skipSelector被标记为deprecatedpublic deprecated是历史遗留的等效符号新代码应使用skipToken。十一、fakeBaseQuery仅使用queryFn时的占位 baseQueryexport function fakeBaseQueryErrorType(): BaseQueryFn void, NEVER, ErrorType, {} 当你的 API只使用queryFn语法例如对接 GraphQL、gRPC 等非 HTTP 场景时可以用fakeBaseQueryMyErrorType()作为baseQuery。其实现是一个恒抛错的函数见 fakeBaseQuery.tsreturn function () { throw new Error( When using fakeBaseQuery, all queries mutations must use the queryFn definition syntax., ) }NEVER类型是内部Symbol_NEVER用于表达该 baseQuery 永远不返回数据。它带来的最大价值是可以为所有queryFn声明一个统一的错误类型ErrorType从而获得精确的类型推断。十二、copyWithStructuralSharing结构化共享export function copyWithStructuralSharingT(oldObj: any, newObj: T): T该函数是 RTK Query默认开启structuralSharing: true时的底层工具utils/copyWithStructuralSharing.ts它递归对比新旧两棵响应对象逐层复用未变化的部分只有当某层数据确实变化时才创建新对象。这样即使服务端返回了结构相同的响应组件也不会因为引用变化而无谓重渲染。实现要点仅当新旧值都是纯对象或都是数组时才进入递归比较否则直接返回新值。逐 key 递归后若发现所有子值引用都未变化则整体返回旧对象。在 core/buildSlice.ts 中substate.data的赋值会依据端点配置的structuralSharing默认true决定是否调用该函数关闭方式是在 endpoint 上设置structuralSharing: false。十三、从报告看版本演进的注意事项将这份 API 报告与当前源码对照可以归纳出几个对升级/迁移有实际影响的观察点均为仓库内可验证的事实overrideExisting的类型扩展报告为boolean当前源码为boolean | throwapiTypes.tsthrow用于严格模式下避免静默覆盖。CreateApiOptions新增选项当前源码还包含invalidationBehavior: delayed | immediately默认delayed控制 mutation 后 tag 失效的时机createApi.ts、以及 schema 校验相关选项onSchemaFailure、catchSchemaFailure、skipSchemaValidation默认false。FetchBaseQueryError新增TIMEOUT_ERROR变体报告未列出源码已包含。skipSelector已废弃应迁移到skipToken。第三种端点类型infiniteQuery源码与类型导出InfiniteQueryDefinition等见 query/index.ts中已存在对应文档 infinite-queries.mdx。这说明API 报告反映的是生成时刻的公共面当仓库版本迭代后判断最终行为应以 packages/toolkit/src/query 目录下的源码为准。十四、继续深入仓库的路径指引本文涉及的公共 API 均可沿以下路径在仓库内继续阅读类型定义与导出总览packages/toolkit/src/query/index.tsApi/ApiModules/Module类型packages/toolkit/src/query/apiTypes.tsBaseQueryFn/BaseQueryApi/QueryReturnValuepackages/toolkit/src/query/baseQueryTypes.tscreateApi/buildCreateApi实现packages/toolkit/src/query/createApi.tsfetchBaseQuery实现与错误类型packages/toolkit/src/query/fetchBaseQuery.tsretry实现packages/toolkit/src/query/retry.tssetupListeners实现packages/toolkit/src/query/core/setupListeners.tsskipToken/QueryStatuspackages/toolkit/src/query/core/buildSelectors.ts、packages/toolkit/src/query/core/apiState.tsfakeBaseQuerypackages/toolkit/src/query/fakeBaseQuery.tsdefaultSerializeQueryArgspackages/toolkit/src/query/defaultSerializeQueryArgs.tscopyWithStructuralSharingpackages/toolkit/src/query/utils/copyWithStructuralSharing.ts配套概念文档docs/rtk-query/api/createApi.mdx、docs/rtk-query/api/fetchBaseQuery.mdx、docs/rtk-query/usage/error-handling.mdx、docs/rtk-query/usage/infinite-queries.mdx结语rtk-query.api.md这份机器生成的报告虽然不提供使用教程但它以最精确的方式锁定了 RTK Query 的全部公共类型签名与导出。本文基于它梳理了从createApi配置、BaseQueryFn契约、fetchBaseQuery行为到retry、setupListeners、skipToken等工具的完整图景并通过源码逐项验证了默认值与实现细节。对开发者而言把 API 报告当作公共面索引、把源码当作行为真相是驾驭 RTK Query 类型系统与扩展机制最可靠的方法。【免费下载链接】redux-toolkitThe official, opinionated, batteries-included toolset for efficient Redux development项目地址: https://gitcode.com/gh_mirrors/re/redux-toolkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表