在 RTK Query(Redux Toolkit Query)中,tagTypes 和 providesTags(以及 mutation 中的 invalidatesTags)是缓存失效(cache invalidation)机制的核心部分,它们分工不同,看似“重复”其实是为了实现类型安全、灵活的粒度控制和自动重获取。
为什么需要先定义 tagTypes?
- tagTypes 是你在
createApi时全局声明的标签类型列表(数组字符串),例如:const api = createApi({ tagTypes: ['Post', 'User'], // 这里声明可能的标签类型 // ... }); - 作用:
- 定义该 API slice 中允许使用的所有标签类型。
- 提供 TypeScript 类型检查:在 endpoints 中使用 providesTags/invalidatesTags 时,标签的
type必须来自 tagTypes,否则会报类型错误(避免拼写错或随意发明标签)。 - 它是静态声明,让 RTK Query 知道这个 API 涉及哪些实体类型的数据,便于内部管理和代码分割(injectEndpoints 时也可通过 enhanceEndpoints 添加)。
如果不定义 tagTypes,直接在 providesTags 用字符串,TypeScript 会报错(type 被推断为 never 或 string 不匹配)。
为什么还要在每个 endpoint 定义 providesTags?
- providesTags 是动态的,在具体 endpoint(通常是 query)中声明这个 endpoint 的缓存数据提供了哪些标签。
- 例如:
getPosts: build.query({ query: () => '/posts', providesTags: (result) => result ? [...result.map(({ id }) => ({ type: 'Post', id })), { type: 'Post', id: 'LIST' }] : [{ type: 'Post', id: 'LIST' }], }), - 作用:
- 为当前查询的缓存条目附加标签,告诉 RTK Query “这个缓存数据依赖于这些标签”。
- 支持细粒度控制:
- 简单字符串如
'Post':表示整个列表或所有 Post 数据(无效化时会重获取全部)。 - 带 id 的对象
{ type: 'Post', id: 123 }:表示具体某个 Post 实例(无效化时只重获取该项,避免不必要的全量重获取)。
- 简单字符串如
- 可以是函数,根据返回结果动态生成标签(常见于列表查询)。
它们如何协作实现自动重获取?
- Query endpoint 用 providesTags 标记缓存“提供”了哪些标签。
- Mutation endpoint 用 invalidatesTags 标记这个操作会“失效”哪些标签。
- 当 mutation 执行成功后,RTK Query 会自动查找所有提供了匹配标签的缓存条目,并:
- 如果组件还在订阅该数据 → 自动重获取(refetch)。
- 否则 → 从缓存移除。
示例:
- getPosts providesTags: ['Post'] 或带 id 的标签。
- addPost mutation invalidatesTags: ['Post'] → 添加新帖后,自动重获取帖子列表。
总结为什么“都需要”
- tagTypes:全局、静态,负责类型安全和标签规范(像“枚举”)。
- providesTags:局部、动态,负责具体缓存关联和失效粒度(像“实例标注”)。
- 没有 tagTypes,providesTags 就失去类型保护;没有 providesTags,mutation 的 invalidatesTags 就不知道要失效哪些缓存。
这种设计让缓存管理既安全又强大,避免手动 refetch,同时支持从“全列表失效”到“单个项失效”的灵活场景。更多细节可参考官方文档的 Automated Re-fetching 部分。