mcpField
T
tanstack-query

tanstack-query

github.com/exceptionless开发2.5k53 次安装3 天前更新
代码仓库

关于 tanstack-query

面向 Svelte 应用开发者的 TanStack Query 数据请求实践,涵盖 API 集中管理、查询键约定、变更操作及 WebSocket 驱动的缓存失效。

主要功能

  1. 01按功能集中管理 API 调用
  2. 02使用查询键工厂
  3. 03查询与变更命名约定
  4. 04乐观更新和失败回滚
  5. 05基于 WebSocket 消息失效缓存

适用场景

  1. 01为功能模块组织数据请求
  2. 02统一管理查询键和缓存更新
  3. 03实现带回滚的乐观变更
  4. 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}QuerygetOrganizationsQuery()
创建(POST)post{Resource}MutationpostOrganizationMutation()
更新(PATCH)patch{Resource}MutationpatchOrganizationMutation()
删除(DELETE)delete{Resource}MutationdeleteOrganizationMutation()

依赖查询

使用 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