人人都会AI编程

核心能力:数据缓存、自动重取、乐观更新、分页 / 无限滚动

更新时间:2026-07-11

TanStack Query(前身 React Query)是目前 React 生态中最主流的服务端状态管理库。它把“服务端数据”视作一种特殊的状态,提供了一套完整的获取、缓存、同步和更新机制。以下四个核心能力解决了前端数据请求中最常见的痛点。


1. 数据缓存

痛点: 传统方式下,每次组件挂载或页面切换都会重新请求数据,导致不必要的网络开销和加载闪烁。

TanStack Query 如何解决:
每个接口请求都由唯一的 queryKey 标识,返回的数据会被存入内存缓存。当同一个 queryKey 再次被使用时,直接从缓存返回数据,同时可以选择在后台重新发起请求以保持数据新鲜。这避免了重复请求,也让页面切换瞬间呈现已有数据,体验如同本地数据。

import { useQuery } from '@tanstack/react-query';

function UserProfile({ userId }) {
  const { data } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetch(`/api/users/${userId}`).then(res => res.json()),
    staleTime: 5 * 60 * 1000, // 5分钟内缓存视为新鲜
  });

  return <div>{data?.name}</div>;
}

实用要点:

  • staleTime 控制数据“新鲜度”,在此时间内不会触发后台重取。
  • cacheTime(v5 中改为 gcTime)控制缓存垃圾回收时间,组件卸载后数据仍保留一段时间,再次挂载可立即使用。

2. 自动重取

痛点: 数据过期、窗口重新聚焦、网络恢复等场景下,需要手动刷新数据。

TanStack Query 如何解决:
提供多种自动重新获取策略,保证用户看到的始终是“尽力最新”的数据。默认情况下:

  • 数据变为“stale”时,下一次查询会自动后台重取。
  • 浏览器窗口重新获得焦点(refetchOnWindowFocus)时重取。
  • 网络断开后重新连接时重取。
const { data } = useQuery({
  queryKey: ['notifications'],
  queryFn: fetchNotifications,
  refetchInterval: 10000, // 每10秒自动轮询
});

实用要点:

  • 可按需关闭自动重取(如对实时性要求不高的数据),通过配置 refetchOnWindowFocus: false 等。
  • 提供 refetchInterval 用于轮询场景,代码简洁,无需自己管理定时器。

3. 乐观更新

痛点: 用户提交操作(如添加、删除、修改)后,需要等待服务端响应才更新 UI,这会产生明显的操作延迟。

TanStack Query 如何解决:
通过 useMutation,你可以在请求发起前 预先修改缓存数据,从而立即更新 UI。如果服务端请求失败,自动回滚到修改前的状态。

const queryClient = useQueryClient();
const mutation = useMutation({
  mutationFn: (newTodo) => fetch('/api/todos', { method: 'POST', body: newTodo }),
  onMutate: async (newTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos'] });
    const previousTodos = queryClient.getQueryData(['todos']);
    // 乐观地将新项插入缓存
    queryClient.setQueryData(['todos'], old => [...old, newTodo]);
    return { previousTodos }; // 用于回滚
  },
  onError: (err, newTodo, context) => {
    // 回滚到乐观更新前的状态
    queryClient.setQueryData(['todos'], context.previousTodos);
  },
  onSettled: () => {
    // 无论成功或失败,最终从服务端重新获取以保证数据一致
    queryClient.invalidateQueries({ queryKey: ['todos'] });
  },
});

实用要点:

  • 乐观更新极大提升交互流畅度,尤其适用于即时反馈场景(如点赞、删除评论)。
  • 必须实现回滚机制,否则网络错误会导致 UI 与服务端状态不一致。
  • 简单场景也可直接使用 mutation.onSuccess 手动更新缓存,避免复杂控制。

4. 分页 / 无限滚动

痛点: 传统分页需要手动管理当前页码、总页数、翻页逻辑;无限滚动还需要处理加载更多、判断是否到底等复杂边界。

TanStack Query 如何解决:

  • 分页useQuery 将页码放入 queryKey,切换页码时自动请求新数据,缓存独立,前进后退体验极佳。keepPreviousData(v5 中为 placeholderData)让翻页时显示旧数据,避免页面抖动。
  • 无限滚动useInfiniteQuery 专门处理“加载更多”场景,提供 fetchNextPagehasNextPageisFetchingNextPage 等属性,配合滚动事件即可实现。

分页示例:

const [page, setPage] = useState(1);
const { data, isLoading } = useQuery({
  queryKey: ['projects', page],
  queryFn: () => fetch(`/api/projects?page=${page}`).then(res => res.json()),
  placeholderData: keepPreviousData, // 翻页时保持旧数据
});

无限滚动示例:

const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
  queryKey: ['posts'],
  queryFn: ({ pageParam = 1 }) => fetch(`/api/posts?cursor=${pageParam}`),
  getNextPageParam: (lastPage) => lastPage.nextCursor || undefined,
});

// 配合 Intersection Observer,当底部哨兵出现时加载下一页

实用要点:

  • 列表数据建议使用 useInfiniteQuery 替代传统“加载更多”按钮,尤其是移动端。
  • 分页场景使用 placeholderData + isPlaceholderData 标识,可在数据未返回时显示上一个页面的数据或骨架屏。
  • 两种模式都内置了请求去重、缓存和自动管理,开发者只需关注数据获取逻辑。

TanStack Query 通过上述能力,将服务端状态管理从“命令式请求处理”提升为“声明式数据同步”,显著简化了前端数据交互逻辑,同时带来更流畅的用户体验。