☰
TanStack Query 现代数据获取模式指南:基于 useSuspenseQuery 的缓存优先架构实战
2026/10/9 1:04:32 网站建设 项目流程
  • AI 技能
  • AI 插件
  • 人工智能
  • 开发工具

【免费下载链接】claude-code-infrastructure-showcase

Examples of my Claude Code infrastructure with skill auto-activation, hooks, and agents

项目地址:https://gitcode.com/gh_mirrors/cl/claude-code-infrastructure-showcase
点击查看免费下载

本文档源自 claude-code-infrastructure-showcase 仓库中 frontend-dev-guidelines 技能的>import { useSuspenseQuery } from '@tanstack/react-query'; import { myFeatureApi } from '../api/myFeatureApi'; export const MyComponent: React.FC<Props> = ({ id }) => { // No isLoading - Suspense handles it! const { data } = useSuspenseQuery({ queryKey: ['myEntity', id], queryFn: () => myFeatureApi.getEntity(id), }); // data is ALWAYS defined here (not undefined | Data) return <div>{data.name}</div>; }; // Wrap in Suspense boundary <SuspenseLoader> <MyComponent id={123} /> </SuspenseLoader>

关键点:在useSuspenseQuery下,data的类型是确定已定义的(而非Data | undefined),这是与useQuery最直观的类型差异,配合严格 TypeScript 模式(见 typescript-standards.md)可以消除大量空值判断。

1.3 useSuspenseQuery vs useQuery

特性useSuspenseQueryuseQuery
加载状态由 Suspense 处理手动isLoading检查
数据类型始终已定义Data \| undefined
适用场景Suspense 边界内传统组件
推荐对象新组件仅遗留代码
错误处理Error Boundary手动 error state

何时仍使用常规 useQuery:

  • 维护遗留代码时(渐进改造,不强行重写);
  • 非常简单的、未接入 Suspense 的场景;
  • 需要轮询(polling)配合后台更新(background updates)的场景。

规则:新组件一律优先useSuspenseQuery。


二、缓存优先(Cache-First)策略

2.1 设计动机

React Query 本身带有内存缓存,但默认的useQuery在组件挂载时仍然会发起请求(除非命中 staleTime 判定为 fresh)。缓存优先策略进一步把"先从已有列表缓存中取数、取不到再打 API"的逻辑显式写进queryFn,从而最大化复用已加载的数据、显著减少重复请求。

2.2 缓存优先模式示例

import { useSuspenseQuery, useQueryClient } from '@tanstack/react-query'; import { postApi } from '../api/postApi'; export function useSuspensePost(postId: number) { const queryClient = useQueryClient(); return useSuspenseQuery({ queryKey: ['post', postId], queryFn: async () => { // Strategy 1: Try to get from list cache first const cachedListData = queryClient.getQueryData<{ posts: Post[] }>([ 'posts', 'list' ]); if (cachedListData?.posts) { const cachedPost = cachedListData.posts.find( (post) => post.id === postId ); if (cachedPost) { return cachedPost; // Return from cache! } } // Strategy 2: Not in cache, fetch from API return postApi.getPost(postId); }, staleTime: 5 * 60 * 1000, // Consider fresh for 5 minutes gcTime: 10 * 60 * 1000, // Keep in cache for 10 minutes refetchOnWindowFocus: false, // Don't refetch on focus }); }

要点说明:

  • 先查列表/网格缓存,再决定是否发 API 请求:进入详情页时,如果列表页的数据还在缓存中,直接复用,实现"秒开";
  • 避免冗余请求:同一实体在列表页与详情页之间跳转时不重复拉取;
  • staleTime:数据被认为"新鲜"的时长,在新鲜期内不会重新请求;
  • gcTime:未被使用的数据在缓存中保留的时长(v5 中由原cacheTime更名而来);
  • refetchOnWindowFocus: false:窗口聚焦时不自动刷新,属于用户偏好设定(本仓库的默认 QueryClient 配置也一致采用该值,见下文"查询配置最佳实践")。

2.3 生产级完整示例

在文档的"Complete Examples"中给出了带博客域(blogId + postId)双参数、同时命中多个视图缓存的完整实现:

import { useSuspenseQuery, useQueryClient } from '@tanstack/react-query'; import { postApi } from '../api/postApi'; import type { Post } from '../types'; /** * Hook with cache-first strategy * Checks grid cache before API call */ export function useSuspensePost(blogId: number, postId: number) { const queryClient = useQueryClient(); return useSuspenseQuery<Post, Error>({ queryKey: ['post', blogId, postId], queryFn: async () => { // 1. Check grid cache first const gridCache = queryClient.getQueryData<{ rows: Post[] }>([ 'posts-v2', blogId, 'summary' ]) || queryClient.getQueryData<{ rows: Post[] }>([ 'posts-v2', blogId, 'flat' ]); if (gridCache?.rows) { const cached = gridCache.rows.find(row => row.S_ID === postId); if (cached) { return cached; // Reuse grid data } } // 2. Not in cache, fetch directly return postApi.getPost(blogId, postId); }, staleTime: 5 * 60 * 1000, gcTime: 10 * 60 * 1000, refetchOnWindowFocus: false, }); }

收益:避免重复 API 调用;数据若已加载则即时可用;缓存未命中时自动回退到 API 请求。


三、并行数据获取:useSuspenseQueries

当需要同时获取多个互相独立的数据源时,使用useSuspenseQueries一次性声明所有查询,由单个 Suspense 边界统一管理加载:

import { useSuspenseQueries } from '@tanstack/react-query'; export const MyComponent: React.FC = () => { const [userQuery, settingsQuery, preferencesQuery] = useSuspenseQueries({ queries: [ { queryKey: ['user'], queryFn: () => userApi.getCurrentUser(), }, { queryKey: ['settings'], queryFn: () => settingsApi.getSettings(), }, { queryKey: ['preferences'], queryFn: () => preferencesApi.getPreferences(), }, ], }); // All data available, Suspense handles loading const user = userQuery.data; const settings = settingsQuery.data; const preferences = preferencesQuery.data; return <Display user={user} settings={settings} prefs={preferences} />; };

收益:所有查询并行发出(不串行等待);单一 Suspense 边界即可;返回结果类型安全(每个 query 的结果对象可单独解构)。

文档还给出了仪表盘(Dashboard)形态的完整示例:statsQuery、projectsQuery、notificationsQuery三路并行,各自的数据分别喂给StatsCard、ProjectsList、Notifications组件。


四、Query Keys 组织规范

4.1 命名约定

Query Key 是全应用共享的"缓存地址",必须可预测、可检索、可精确失效。规范格式如下:

// Entity list ['entities', blogId] ['entities', blogId, 'summary'] // With view mode ['entities', blogId, 'flat'] // Single entity ['entity', blogId, entityId] // Related data ['entity', entityId, 'history'] ['entity', entityId, 'comments'] // User-specific ['user', userId, 'profile'] ['user', userId, 'permissions']

命名规则:

  • 以实体名开头:列表用复数(entities、posts),单个实体用单数(entity、post);
  • 包含 ID 以保证唯一性:blogId、entityId、userId等逐级追加;
  • 视图模式/关系词放在末尾:'summary'、'flat'、'history'、'comments'等;
  • 全应用保持一致:Query Key 的构造方式必须统一,否则缓存失效将无法命中。

4.2 实际 Key 示例与失效模式

// From useSuspensePost.ts queryKey: ['post', blogId, postId] queryKey: ['posts-v2', blogId, 'summary'] // Invalidation patterns queryClient.invalidateQueries({ queryKey: ['post', blogId] }); // All posts for form queryClient.invalidateQueries({ queryKey: ['post'] }); // All posts

注意失效(invalidate)是前缀匹配的:['post', blogId]会失效该 blogId 下所有 post 相关查询,['post']则失效所有 post 查询。这一特性决定了 Key 的层级设计需要兼顾"精确失效单条"与"批量失效列表"两种需求——这也正是缓存优先 + 失效模式能在本仓库的网格(grid/list)场景中高效协作的原因。


五、API Service 层模式

5.1 文件结构

每个功能模块(feature)下建立集中的 API 服务文件,作为组件与网络层之间的唯一桥梁:

features/ my-feature/ api/ myFeatureApi.ts # Service layer

该结构与 SKILL.md 中"New Feature Checklist"的目录约定一致:features/{feature-name}/下包含api/、components/、hooks/、helpers/、types/五个子目录。

5.2 服务层模式(源自 postApi.ts)

/** * Centralized API service for my-feature operations * Uses apiClient for consistent error handling */ import apiClient from '@/lib/apiClient'; import type { MyEntity, UpdatePayload } from '../types'; export const myFeatureApi = { /** * Fetch a single entity */ getEntity: async (blogId: number, entityId: number): Promise<MyEntity> => { const { data } = await apiClient.get( `/blog/entities/${blogId}/${entityId}` ); return data; }, /** * Fetch all entities for a form */ getEntities: async (blogId: number, view: 'summary' | 'flat'): Promise<MyEntity[]> => { const { data } = await apiClient.get( `/blog/entities/${blogId}`, { params: { view } } ); return data.rows; }, /** * Update entity */ updateEntity: async ( blogId: number, entityId: number, payload: UpdatePayload ): Promise<MyEntity> => { const { data } = await apiClient.put( `/blog/entities/${blogId}/${entityId}`, payload ); return data; }, /** * Delete entity */ deleteEntity: async (blogId: number, entityId: number): Promise<void> => { await apiClient.delete(`/blog/entities/${blogId}/${entityId}`); }, };

关键规范:

  • 导出一个包含方法的单一对象,按功能聚合(getEntity / getEntities / updateEntity / deleteEntity);
  • 统一使用apiClient(@/lib/apiClient导出的 axios 实例),禁止在服务层自行 new axios;
  • 参数与返回值全部类型化,并声明Promise<MyEntity>等显式返回类型;
  • 每个方法带 JSDoc 注释,便于 Claude Code / IDE 理解意图;
  • 错误处理集中化:由 apiClient 的拦截器统一处理,服务层不再重复 try/catch。

本仓库的 SKILL.md 在"Data Fetching"一节同样强调:API Service 层应创建于features/{feature}/api/{feature}Api.ts,使用apiClient,并为每个 feature 提供集中式方法。


六、路由格式规则(重要)

文档特别以"前缀式微服务路由"为例说明了路径写法——原生产项目中,代理(proxy)将每个微服务映射到路径前缀,因此API 路径中不需要/api/前缀:

// ✅ CORRECT - Path matches the proxy configuration (here: direct service prefixes) await apiClient.get('/blog/posts/123'); await apiClient.post('/projects/create', data); await apiClient.put('/users/update/456', updates); await apiClient.get('/email/templates'); // ❌ WRONG - In this setup the proxy adds no /api/ prefix await apiClient.get('/api/blog/posts/123'); // WRONG here (fine if YOUR backend uses /api/) await apiClient.post('/api/projects/create', data); // WRONG here

示例微服务路由映射(来自原项目):

  • 表单服务:/blog/*
  • 项目服务:/projects/*
  • 邮件服务:/email/*
  • 用户服务:/users/*

为什么:在该架构中,API 路由由代理配置处理,所以不需要/api/前缀。真正的规则是:让apiClient中的路径与你自己项目的路由配置保持一致——如果后端在/api/前缀下提供路由,则使用/api/...也是正确的。文档中的/blog/...仅是教学示例,落地时请替换为你自己的路径。


七、Mutations(变更操作)

7.1 基础 Mutation 模式

import { useMutation, useQueryClient } from '@tanstack/react-query'; import { myFeatureApi } from '../api/myFeatureApi'; import { useMuiSnackbar } from '@/hooks/useMuiSnackbar'; export const MyComponent: React.FC = () => { const queryClient = useQueryClient(); const { showSuccess, showError } = useMuiSnackbar(); const updateMutation = useMutation({ mutationFn: (payload: UpdatePayload) => myFeatureApi.updateEntity(blogId, entityId, payload), onSuccess: () => { // Invalidate and refetch queryClient.invalidateQueries({ queryKey: ['entity', blogId, entityId] }); showSuccess('Entity updated successfully'); }, onError: (error) => { showError('Failed to update entity'); console.error('Update error:', error); }, }); const handleUpdate = () => { updateMutation.mutate({ name: 'New Name' }); }; return ( <Button onClick={handleUpdate} disabled={updateMutation.isPending} > {updateMutation.isPending ? 'Updating...' : 'Update'} </Button> ); };

要点:mutationFn调用 Service 层;onSuccess中精确失效对应 Query Key并触发重新获取;用户反馈统一走useMuiSnackbar(本仓库明确禁止使用react-toastify,见 loading-and-error-states.md);按钮通过isPending禁用并切换文案。

7.2 乐观更新(Optimistic Updates)

const updateMutation = useMutation({ mutationFn: (payload) => myFeatureApi.update(id, payload), // Optimistic update onMutate: async (newData) => { // Cancel outgoing refetches await queryClient.cancelQueries({ queryKey: ['entity', id] }); // Snapshot current value const previousData = queryClient.getQueryData(['entity', id]); // Optimistically update queryClient.setQueryData(['entity', id], (old) => ({ ...old, ...newData, })); // Return rollback function return { previousData }; }, // Rollback on error onError: (err, newData, context) => { queryClient.setQueryData(['entity', id], context.previousData); showError('Update failed'); }, // Refetch after success or error onSettled: () => { queryClient.invalidateQueries({ queryKey: ['entity', id] }); }, });

乐观更新的标准三步:onMutate 快照回滚(先取消在途请求 → 读取旧值 → 立即写入新值 → 返回快照作为 context)→onError 回滚(把 context 中的旧值写回,并提示错误)→onSettled 刷新(无论成败都失效查询,保证与服务端最终一致)。

7.3 带缓存失效的更新与删除(生产完整版)

更新 Post:onSuccess中同时失效单条 post(['post', blogId, postId])与列表/网格(['posts-v2', blogId]),保证详情与列表同步刷新:

export const useUpdatePost = () => { const queryClient = useQueryClient(); const { showSuccess, showError } = useMuiSnackbar(); return useMutation({ mutationFn: ({ blogId, postId, data }: UpdateParams) => postApi.updatePost(blogId, postId, data), onSuccess: (data, variables) => { // Invalidate specific post queryClient.invalidateQueries({ queryKey: ['post', variables.blogId, variables.postId] }); // Invalidate list to refresh grid queryClient.invalidateQueries({ queryKey: ['posts-v2', variables.blogId] }); showSuccess('Post updated'); }, onError: (error) => { showError('Failed to update post'); console.error('Update error:', error); }, }); };

删除 Post:onSuccess中手动从缓存中移除该行(乐观删行),onError中失效列表回滚到真实状态:

export const useDeletePost = () => { const queryClient = useQueryClient(); const { showSuccess, showError } = useMuiSnackbar(); return useMutation({ mutationFn: ({ blogId, postId }: DeleteParams) => postApi.deletePost(blogId, postId), onSuccess: (data, variables) => { // Remove from cache manually (optimistic) queryClient.setQueryData<{ rows: Post[] }>( ['posts-v2', variables.blogId], (old) => ({ ...old, rows: old?.rows.filter(row => row.S_ID !== variables.postId) || [] }) ); showSuccess('Post deleted'); }, onError: (error, variables) => { // Rollback - refetch to get accurate state queryClient.invalidateQueries({ queryKey: ['posts-v2', variables.blogId] }); showError('Failed to delete post'); }, }); };

八、高级查询模式

8.1 预取(Prefetching)

在用户真正导航到详情页之前提前把数据放入缓存:

export function usePrefetchEntity() { const queryClient = useQueryClient(); return (blogId: number, entityId: number) => { return queryClient.prefetchQuery({ queryKey: ['entity', blogId, entityId], queryFn: () => myFeatureApi.getEntity(blogId, entityId), staleTime: 5 * 60 * 1000, }); }; } // Usage: Prefetch on hover <div onMouseEnter={() => prefetch(blogId, id)}> <Link to={`/entity/${id}`}>View</Link> </div>

prefetchQuery与useSuspenseQuery共享同一缓存,预取完成后,进入详情页将直接命中缓存实现瞬时渲染。常见触发时机:hover、鼠标按下、路由 enter 前等。

8.2 仅读缓存、不发请求

export function useEntityFromCache(blogId: number, entityId: number) { const queryClient = useQueryClient(); // Get from cache, don't fetch if missing const directCache = queryClient.getQueryData<MyEntity>(['entity', blogId, entityId]); if (directCache) return directCache; // Try grid cache const gridCache = queryClient.getQueryData<{ rows: MyEntity[] }>(['entities-v2', blogId]); return gridCache?.rows.find(row => row.id === entityId); }

此模式适合"有缓存就用、没有就返回 undefined 走降级 UI"的场景,例如面包屑、行内快捷信息等,绝不触发网络请求。

8.3 依赖查询(Dependent Queries)

// Fetch user first, then user's settings const { data: user } = useSuspenseQuery({ queryKey: ['user', userId], queryFn: () => userApi.getUser(userId), }); const { data: settings } = useSuspenseQuery({ queryKey: ['user', userId, 'settings'], queryFn: () => settingsApi.getUserSettings(user.id), // Automatically waits for user to load due to Suspense });

由于 Suspense 语义,第二个查询的queryFn会自动等待第一个查询的数据就绪(user.id可用)后才执行——无需手动维护 enabled 标志,这也是比传统useQuery依赖链更简洁的地方。


九、apiClient 配置

9.1 统一使用 apiClient

import apiClient from '@/lib/apiClient'; // apiClient is a configured axios instance // Automatically includes: // - Base URL configuration // - Cookie-based authentication // - Error interceptors // - Response transformers

规范:不要创建新的 axios 实例,一律使用apiClient,以保证:基础 URL 配置统一、基于 Cookie 的认证自动附加、错误拦截器集中处理、响应转换器(如统一解包data)全局生效。

9.2 典型错误处理回调

import { useMuiSnackbar } from '@/hooks/useMuiSnackbar'; const { showError } = useMuiSnackbar(); const { data } = useSuspenseQuery({ queryKey: ['entity', id], queryFn: () => myFeatureApi.getEntity(id), // Handle errors onError: (error) => { showError('Failed to load entity'); console.error('Load error:', error); }, });

9.3 与 Error Boundary 结合

import { ErrorBoundary } from 'react-error-boundary'; <ErrorBoundary fallback={<ErrorDisplay />} onError={(error) => console.error(error)} > <SuspenseLoader> <ComponentWithSuspenseQuery /> </SuspenseLoader> </ErrorBoundary>

整体错误策略是双层结构:查询级onError做细粒度提示(Snackbar 反馈 + 控制台日志),组件级 Error Boundary 兜底未捕获的渲染错误,实现覆盖完整、体验一致的错误处理。


十、查询配置最佳实践

10.1 默认配置(QueryClientProvider 初始化处)

// In QueryClientProvider setup const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 1000 * 60 * 5, // 5 minutes gcTime: 1000 * 60 * 10, // 10 minutes (was cacheTime) refetchOnWindowFocus: false, // Don't refetch on focus refetchOnMount: false, // Don't refetch on mount if fresh retry: 1, // Retry failed queries once }, }, });

这些默认值的含义:数据 5 分钟内视为新鲜(不重新请求);未被使用的数据在缓存中保留 10 分钟;窗口聚焦不自动刷新;挂载时若数据仍新鲜则不重新获取;失败查询仅重试 1 次(避免在弱网下放大请求压力)。

10.2 按查询覆盖默认值

// Frequently changing data - shorter staleTime useSuspenseQuery({ queryKey: ['notifications', 'unread'], queryFn: () => notificationApi.getUnread(), staleTime: 30 * 1000, // 30 seconds }); // Rarely changing data - longer staleTime useSuspenseQuery({ queryKey: ['form', blogId, 'structure'], queryFn: () => formApi.getStructure(blogId), staleTime: 30 * 60 * 1000, // 30 minutes });

经验法则:数据变化越频繁,staleTime越短(通知类 30 秒);越稳定的数据staleTime越长(表单结构类 30 分钟),在新鲜度与请求量之间取得平衡。


十一、完整示例

11.1 简单实体获取(用户资料)

import React from 'react'; import { useSuspenseQuery } from '@tanstack/react-query'; import { Box, Typography } from '@mui/material'; import { userApi } from '../api/userApi'; interface UserProfileProps { userId: string; } export const UserProfile: React.FC<UserProfileProps> = ({ userId }) => { const { data: user } = useSuspenseQuery({ queryKey: ['user', userId], queryFn: () => userApi.getUser(userId), staleTime: 5 * 60 * 1000, }); return ( <Box> <Typography variant='h5'>{user.name}</Typography> <Typography>{user.email}</Typography> </Box> ); }; // Usage with Suspense <SuspenseLoader> <UserProfile userId='123' /> </SuspenseLoader>

11.2 并行获取(仪表盘)

import { useSuspenseQueries } from '@tanstack/react-query'; export const Dashboard: React.FC = () => { const [statsQuery, projectsQuery, notificationsQuery] = useSuspenseQueries({ queries: [ { queryKey: ['stats'], queryFn: () => statsApi.getStats(), }, { queryKey: ['projects', 'active'], queryFn: () => projectsApi.getActiveProjects(), }, { queryKey: ['notifications', 'unread'], queryFn: () => notificationsApi.getUnread(), }, ], }); return ( <Box> <StatsCard data={statsQuery.data} /> <ProjectsList projects={projectsQuery.data} /> <Notifications items={notificationsQuery.data} /> </Box> ); };

十二、落地集成指南

12.1 在 Claude Code 工作流中的强制保障

本模式文档并非孤立存在,它属于frontend-dev-guidelines技能的资源文件(data-fetching.md),由 SKILL.md 通过渐进式披露(Progressive Disclosure)机制按需加载:Claude 先读取主文件,仅在处理数据获取任务时加载本资源,避免上下文膨胀(符合仓库的"500 行规则")。

在 skill-rules.json 中,frontend-dev-guidelines被配置为guardrail+block强制执行,fileTriggers.pathPatterns覆盖src/**/*.tsx、frontend/src/**/*.tsx、client/src/**/*.tsx等常见前端目录,contentPatterns命中from '@mui/material';、<Grid、makeStyles、@material-ui/core等特征;一旦命中,blockMessage会要求先通过 Skill 工具激活本技能、检查 MUI v7 模式(Grid 使用size={{}}属性)再继续编辑。这意味着在接入本仓库基础设施的项目中,偏离上述数据获取规范的新代码会被 PreToolUse 钩子直接拦截。

12.2 通过 setup.ts 自动识别前端技术栈

本仓库的安装向导 setup.ts 在detectTechStack函数中自动探测目标项目的技术栈:它检查package.json(含frontend/、client/、web/、api/、server/、backend/等常见位置)中的依赖——命中react标记 React、命中@mui/material标记 MUI、命中@tanstack/react-query或@tanstack/react-router标记 TanStack——并在安装完成后建议首个验证提示词 "create a new React component",从而触发frontend-dev-guidelines技能自动激活。若你的项目启用了 TanStack Query(@tanstack/react-query),本文所述模式即为该技能推荐的数据获取唯一路径。

12.3 迁移到你的项目时的注意事项

  • 文档中的/blog/*、/projects/*等路径来自原生产项目的微服务代理配置,请替换为你自己的路由前缀(若后端在/api/下提供路由,则用/api/...);
  • apiClient、SuspenseLoader、useMuiSnackbar是项目内部模块(对应 SKILL.md 中 Import Aliases 表格里的@/lib/apiClient、~components/SuspenseLoader、@/hooks/useMuiSnackbar),接入时需在你的代码库中实现等价组件;
  • Query Key 命名与缓存优先策略依赖团队全员遵守同一约定,建议将本文直接纳入技能资源与代码评审检查单。

十三、总结:现代数据获取模式速查配方

Modern Data Fetching Recipe:

  1. 创建 API Service:features/X/api/XApi.ts,统一使用apiClient;
  2. 使用 useSuspenseQuery:新组件一律在SuspenseLoader包裹下使用;
  3. 缓存优先:queryFn内先检查网格/列表缓存,命中即返回,未命中再发 API;
  4. Query Keys 一致命名:['entity', id],列表复数、单条单数、视图/关系词置尾;
  5. 路由格式:/blog/route(匹配你的代理配置),不是/api/blog/route;
  6. Mutations:成功后invalidateQueries精确失效对应 Key(含列表);
  7. 错误处理:onError回调 +useMuiSnackbar提示 + Error Boundary 兜底;
  8. 类型安全:所有参数与返回值显式标注类型。

关联阅读:

  • component-patterns.md — Suspense 与组件模式集成
  • loading-and-error-states.md — SuspenseLoader 与错误状态规范
  • complete-examples.md — 完整可运行示例
  • common-patterns.md — 表单、认证、DataGrid 等常见模式
  • frontend-dev-guidelines/SKILL.md — 技能主文件与导航
  • skill-rules.json — 技能触发与强制规则配置
  • AI 技能
  • AI 插件
  • 人工智能
  • 开发工具

【免费下载链接】claude-code-infrastructure-showcase

Examples of my Claude Code infrastructure with skill auto-activation, hooks, and agents

项目地址:https://gitcode.com/gh_mirrors/cl/claude-code-infrastructure-showcase
点击查看免费下载
上一篇:如何5分钟解锁英雄联盟全皮肤:R3nzSkin国服特供版完全指南
下一篇:KMS_VL_ALL_AIO:Windows与Office企业级批量激活机制的技术实现与架构解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询