关于 tanstack-query
面向 Svelte 应用开发者的 TanStack Query 数据请求实践,涵盖 API 集中管理、查询键约定、变更操作及 WebSocket 驱动的缓存失效。
主要功能
- 01按功能集中管理 API 调用
- 02使用查询键工厂
- 03查询与变更命名约定
- 04乐观更新和失败回滚
- 05基于 WebSocket 消息失效缓存
适用场景
- 01为功能模块组织数据请求
- 02统一管理查询键和缓存更新
- 03实现带回滚的乐观变更
- 04根据 WebSocket 消息刷新查询
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ exceptionless/ exceptionless --skill tanstack-query安装后在 Claude Code 中输入/tanstack-query确认已加载。
详细介绍
TanStack Query
文档: tanstack.com/query。本地模式不足以解决问题时,请参考官方文档。
按功能在 api.svelte.ts 中集中处理 API 调用,使用 TanStack Query 和 @exceptionless/fetchclient。
查询基础
// src/lib/features/organizations/api.svelte.ts
import { createQuery, createMutation, useQueryClient } from "@tanstack/svelte-query";
import { type FetchClientResponse, type ProblemDetails, useFetchClient } from "@exceptionless/fetchclient";
import { accessToken } from "$features/auth/index.svelte";
const queryKeys = {
type: ["Organization"] as const,
};
export function getOrganizationsQuery() {
return createQuery<FetchClientResponse<Organization[]>, ProblemDetails>(() => ({
enabled: () => !!accessToken.current,
queryKey: queryKeys.type,
queryFn: async ({ signal }: { signal: AbortSignal }) => {
const client = useFetchClient();
const response = await client.getJSON<Organization[]>("/organizations", { signal });
return response;
},
}));
}
查询键约定
每个功能使用一个 queryKeys 工厂,以确保类型安全和一致性:
export const queryKeys = {
type: ["Webhook"] as const,
id: (id: string | undefined) => [...queryKeys.type, id] as const,
ids: (ids: string[] | undefined) => [...queryKeys.type, ...(ids ?? [])] as const,
project: (id: string | undefined) => [...queryKeys.type, "project", id] as const,
deleteWebhook: (ids: string[] | undefined) => [...queryKeys.ids(ids), "delete"] as const,
postWebhook: () => [...queryKeys.type, "post"] as const,
};
优先使用功能的 queryKeys 工厂,而非临时数组,以便 WebSocket 失效处理和缓存更新共用相同的键。
变更
export function postOrganizationMutation() {
const queryClient = useQueryClient();
return createMutation(() => ({
mutationFn: async (data: CreateOrganizationRequest) => {
const client = useFetchClient();
const response = await client.postJSON<Organization>("/organizations", data);
return response.data!;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.type });
},
}));
}
命名约定
| 模式 | 命名 | 示例 |
|---|---|---|
| 查询(GET) | get{Resource}Query | getOrganizationsQuery() |
| 创建(POST) | post{Resource}Mutation | postOrganizationMutation() |
| 更新(PATCH) | patch{Resource}Mutation | patchOrganizationMutation() |
| 删除(DELETE) | delete{Resource}Mutation | deleteOrganizationMutation() |
依赖查询
使用 enabled 条件运行查询:enabled: !!projectId。
乐观更新
对于乐观更新缓存数据的变更:使用 onMutate 取消正在进行的查询,通过 getQueryData 保存之前的值,并通过 setQueryData 应用乐观更新。使用 onError 从快照回滚,并使用 onSettled 始终执行 invalidateQueries,以进行最终重新获取。
WebSocket 驱动的失效处理
收到 WebSocket 消息时使查询失效:
export async function invalidateWebhookQueries(
queryClient: QueryClient,
message: WebSocketMessageValue<"WebhookChanged">,
) {
const { id, organization_id, project_id } = message;
if (id) await queryClient.invalidateQueries({ queryKey: queryKeys.id(id) });
if (project_id) await queryClient.invalidateQueries({ queryKey: queryKeys.project(project_id) });
if (!id && !organization_id && !project_id)
await queryClient.invalidateQueries({ queryKey: queryKeys.type });
}
将应用布局中的 WebSocket 消息接入功能失效处理辅助函数。
评论
支持 Markdown