Skip to content

各方案优缺点

概述

React应用中有多种数据获取方案,每种方案都有其优缺点和适用场景。本文将全面对比Fetch API、Axios、SWR、TanStack Query、GraphQL、tRPC等方案,帮助你选择最适合项目的数据获取策略。

Fetch API

优点

  1. 原生支持

    • 无需安装依赖
    • 浏览器原生API
    • 现代浏览器全面支持
  2. Promise based

    • 支持async/await
    • 链式调用
    • 易于理解
  3. 灵活性高

    • 完全控制请求配置
    • 可自定义封装
    • 适合简单场景

缺点

  1. 功能有限

    • 不支持请求取消(需AbortController)
    • 不支持上传进度
    • 不支持超时配置
    • 不自动转换JSON
  2. 错误处理复杂

    • HTTP错误不会reject
    • 需要手动检查response.ok
    • 错误处理代码冗余
  3. 无状态管理

    • 需手动管理loading、error
    • 无缓存机制
    • 无重试机制

示例对比

jsx
// 问题: 需要大量样板代码
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    const fetchUser = async () => {
      try {
        const response = await fetch(`/api/users/${userId}`);
        
        if (!response.ok) {
          throw new Error('Failed to fetch');
        }
        
        const data = await response.json();
        setUser(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };
    
    fetchUser();
  }, [userId]);
  
  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  return <div>{user?.name}</div>;
}

适用场景

  • 简单的API调用
  • 原型开发
  • 不需要高级功能的小项目
  • 需要完全控制的场景

Axios

优点

  1. 丰富的功能

    • 自动转换JSON
    • 请求/响应拦截器
    • 请求取消
    • 超时配置
    • 上传/下载进度
  2. 良好的错误处理

    • HTTP错误自动reject
    • 统一的错误格式
    • 易于集中处理
  3. 浏览器兼容性

    • 支持旧浏览器
    • 同时支持浏览器和Node.js
  4. 开发体验好

    • API设计优雅
    • 文档完善
    • 社区活跃

缺点

  1. 包体积

    • 比Fetch API大
    • 增加bundle size
  2. 仍需状态管理

    • 需手动管理loading/error
    • 无内置缓存
    • 无自动重新获取
  3. 配置复杂性

    • 拦截器配置可能复杂
    • 需要额外封装

示例对比

jsx
// 改进: 拦截器统一处理
const apiClient = axios.create({
  baseURL: '/api',
  timeout: 10000,
});

apiClient.interceptors.request.use(config => {
  config.headers.Authorization = `Bearer ${getToken()}`;
  return config;
});

apiClient.interceptors.response.use(
  response => response.data,
  error => {
    if (error.response?.status === 401) {
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

// 但仍需手动管理状态
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    apiClient.get(`/users/${userId}`)
      .then(setUser)
      .finally(() => setLoading(false));
  }, [userId]);
  
  return loading ? <div>Loading...</div> : <div>{user?.name}</div>;
}

适用场景

  • 需要高级HTTP功能
  • 需要支持旧浏览器
  • RESTful API应用
  • 不需要复杂缓存策略

SWR

优点

  1. 简洁的API

    • 一行代码完成数据获取
    • 自动状态管理
    • 声明式用法
  2. 智能缓存

    • Stale-While-Revalidate策略
    • 自动去重
    • 聚焦重新验证
  3. 实时体验

    • 自动重新验证
    • 乐观更新
    • 后台数据更新
  4. 轻量级

    • 包体积小(~5KB)
    • 无外部依赖
    • 性能优秀

缺点

  1. 功能相对有限

    • 主要针对GET请求
    • Mutation功能较弱
    • 无内置分页支持(需useSWRInfinite)
  2. 文档和生态

    • 相比React Query文档较少
    • 插件和工具较少
    • 社区规模较小
  3. 类型支持

    • TypeScript支持一般
    • 需要手动定义类型

示例对比

jsx
// 优势: 极简的API
import useSWR from 'swr';

function UserProfile({ userId }) {
  const { data: user, error, isLoading } = useSWR(
    `/api/users/${userId}`,
    fetcher
  );
  
  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error</div>;
  return <div>{user.name}</div>;
}

// 劣势: Mutation需额外处理
function CreateUser() {
  const { mutate } = useSWRConfig();
  
  const handleCreate = async (data) => {
    await fetch('/api/users', { method: 'POST', body: JSON.stringify(data) });
    mutate('/api/users'); // 手动触发revalidate
  };
  
  return <button onClick={handleCreate}>Create</button>;
}

适用场景

  • 数据展示为主的应用
  • 需要实时数据更新
  • 重视包体积
  • 简单的数据获取场景

TanStack Query (React Query)

优点

  1. 功能最全面

    • 完整的查询和变更支持
    • 强大的缓存管理
    • 分页和无限滚动
    • 离线支持
    • DevTools
  2. 开发体验极佳

    • 声明式API
    • 自动状态管理
    • 完善的TypeScript支持
    • 优秀的文档
  3. 高度可配置

    • 灵活的缓存策略
    • 重试机制
    • 乐观更新
    • 查询失效
  4. 生态系统强大

    • 活跃的社区
    • 丰富的插件
    • 持续维护

缺点

  1. 学习曲线

    • 概念较多
    • 配置选项复杂
    • 需要时间熟悉
  2. 包体积

    • 相对较大(~13KB)
    • 功能多导致体积大
  3. 过度工程

    • 简单项目可能过于复杂
    • 配置可能冗余

示例对比

jsx
// 优势: 功能完整
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

function UserList() {
  const queryClient = useQueryClient();
  
  // 查询
  const { data: users } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    staleTime: 5000,
  });
  
  // 变更
  const { mutate: createUser } = useMutation({
    mutationFn: createUserAPI,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });
  
  // 无限滚动
  const {
    data,
    fetchNextPage,
    hasNextPage,
  } = useInfiniteQuery({
    queryKey: ['posts'],
    queryFn: fetchPosts,
    getNextPageParam: (lastPage) => lastPage.nextCursor,
  });
  
  return <div>...</div>;
}

适用场景

  • 中大型应用
  • 数据密集型应用
  • 需要复杂缓存策略
  • 需要完整的数据同步方案

GraphQL (Apollo Client)

优点

  1. 精确数据获取

    • 只请求需要的字段
    • 避免over-fetching
    • 减少网络传输
  2. 强大的类型系统

    • Schema定义
    • 代码生成
    • 完整的类型安全
  3. 高级功能

    • 实时订阅
    • 缓存归一化
    • 乐观更新
    • 分页
  4. 开发体验

    • GraphQL Playground
    • Apollo DevTools
    • 代码补全

缺点

  1. 学习曲线陡峭

    • 需要学习GraphQL
    • Schema设计复杂
    • 查询语法特殊
  2. 复杂度高

    • 服务端配置复杂
    • 需要额外的基础设施
    • N+1查询问题
  3. 包体积大

    • Apollo Client体积较大(~33KB)
    • 依赖较多
  4. 调试困难

    • 错误信息不直观
    • 网络请求难以追踪

示例对比

jsx
// 优势: 精确查询
import { useQuery, gql } from '@apollo/client';

const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
      posts {
        id
        title
      }
    }
  }
`;

function UserProfile({ userId }) {
  const { data, loading } = useQuery(GET_USER, {
    variables: { id: userId },
  });
  
  return <div>{data?.user.name}</div>;
}

// 劣势: 配置复杂
const client = new ApolloClient({
  uri: 'http://localhost:4000/graphql',
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          posts: {
            merge(existing, incoming) {
              return [...existing, ...incoming];
            },
          },
        },
      },
    },
  }),
});

适用场景

  • 复杂的数据关系
  • 移动应用(减少数据传输)
  • 多客户端应用
  • 需要实时功能
  • 团队熟悉GraphQL

tRPC

优点

  1. 端到端类型安全

    • 无需代码生成
    • TypeScript原生支持
    • 自动类型推断
  2. 开发体验最佳

    • 自动补全
    • 重构友好
    • 类型错误编译时发现
  3. 极简API

    • 学习成本低
    • 代码量少
    • 直观易懂
  4. 轻量级

    • 核心包很小(~2KB)
    • 无额外依赖

缺点

  1. TypeScript only

    • 必须使用TypeScript
    • 不支持其他语言客户端
  2. 全栈TypeScript限制

    • 前后端必须共享类型
    • Monorepo或类型共享配置
  3. 功能相对简单

    • 不如GraphQL灵活
    • 查询语言不够表达力
  4. 生态系统新

    • 工具和插件较少
    • 社区相对小

示例对比

jsx
// 优势: 类型安全
import { trpc } from './utils/trpc';

function UserProfile({ userId }: { userId: string }) {
  // 完全类型安全,自动补全
  const { data: user } = trpc.user.getUser.useQuery({ id: userId });
  
  const { mutate } = trpc.user.update.useMutation();
  
  // TypeScript会检查参数类型
  const handleUpdate = () => {
    mutate({
      id: userId,
      name: 'New Name', // 类型检查
    });
  };
  
  return <div>{user?.name}</div>;
}

// 劣势: 仅限TypeScript全栈
// 服务端
export const userRouter = router({
  getUser: publicProcedure
    .input(z.object({ id: z.string() }))
    .query(({ input }) => {
      return db.user.findUnique({ where: { id: input.id } });
    }),
});

适用场景

  • TypeScript全栈项目
  • 内部工具/管理系统
  • 需要极致类型安全
  • 团队全部使用TypeScript
  • 新项目快速开发

方案对比总结表

特性FetchAxiosSWRReact QueryGraphQLtRPC
包体积0KB~13KB~5KB~13KB~33KB~2KB
学习曲线
类型安全极强
状态管理
缓存
乐观更新
无限滚动
实时订阅
DevTools
文档质量一般极好极好
社区规模-
适合场景简单通用实时复杂关系数据TS全栈

选择建议

按项目规模选择

小型项目 (< 10个页面)

  • Fetch API + 自定义Hook
  • SWR

中型项目 (10-50个页面)

  • Axios + SWR
  • TanStack Query

大型项目 (> 50个页面)

  • TanStack Query
  • GraphQL (Apollo Client)
  • tRPC (TypeScript全栈)

按团队技术栈选择

JavaScript团队

  • Axios
  • SWR
  • React Query

TypeScript团队

  • React Query
  • tRPC
  • GraphQL + Codegen

全栈团队

  • tRPC
  • GraphQL

按应用类型选择

数据展示应用

  • SWR
  • React Query

数据密集应用

  • React Query
  • GraphQL

实时应用

  • GraphQL (Subscriptions)
  • tRPC (WebSocket)

移动应用

  • GraphQL (减少数据传输)
  • React Query

总结

选择数据获取方案的关键因素:

  1. 项目规模和复杂度
  2. 团队技术栈和经验
  3. 性能和包体积要求
  4. 类型安全需求
  5. 实时功能需求
  6. 开发效率要求

没有绝对最好的方案,只有最适合的方案。根据项目实际需求选择合适的工具,有时候组合使用多种方案也是不错的选择。

深入对比分析

性能对比

初始加载性能

tsx
// 1. Bundle Size影响
/*
Fetch API:    0KB   (原生)
tRPC:        ~2KB   (最小)
SWR:         ~5KB   (轻量)
Axios:      ~13KB   (适中)
React Query:~13KB   (适中)
GraphQL:    ~33KB   (最大)
*/

// 2. 首次渲染时间对比
// Fetch API - 最快但需要手动处理
function FetchExample() {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    fetch('/api/users').then(res => res.json()).then(setData);
  }, []);
  // 首次渲染: ~50ms
}

// SWR - 极快
function SWRExample() {
  const { data } = useSWR('/api/users', fetcher);
  // 首次渲染: ~60ms
}

// React Query - 稍慢但功能完整
function RQExample() {
  const { data } = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
  // 首次渲染: ~80ms
}

// GraphQL - 最慢但数据精确
function GraphQLExample() {
  const { data } = useQuery(GET_USERS);
  // 首次渲染: ~120ms
}

运行时性能

tsx
// 1. 重渲染次数对比
function PerformanceComparison() {
  // Fetch - 手动控制,最少重渲染
  const [users, setUsers] = useState([]);
  // 重渲染: 2次 (loading → data)
  
  // SWR - 自动优化
  const { data } = useSWR('/api/users', fetcher);
  // 重渲染: 2-3次 (loading → stale → fresh)
  
  // React Query - 可配置
  const { data } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    notifyOnChangeProps: ['data', 'error'], // 减少重渲染
  });
  // 重渲染: 2-4次
  
  // GraphQL - 归一化缓存
  const { data } = useQuery(GET_USERS);
  // 重渲染: 2-3次
}

// 2. 内存占用对比
class MemoryUsage {
  // Fetch - 最小
  fetch = 0.1; // MB
  
  // SWR - 较小
  swr = 0.5; // MB (带缓存)
  
  // React Query - 中等
  reactQuery = 1.2; // MB (完整缓存+DevTools)
  
  // GraphQL - 较大
  graphql = 2.5; // MB (归一化缓存)
  
  // tRPC - 最小
  trpc = 0.3; // MB
}

开发效率对比

tsx
// 1. 代码量对比 - 实现用户列表CRUD

// === Fetch API: ~200 lines ===
function UserCRUDWithFetch() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const fetchUsers = async () => {
    setLoading(true);
    try {
      const res = await fetch('/api/users');
      if (!res.ok) throw new Error('Failed');
      const data = await res.json();
      setUsers(data);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };
  
  const createUser = async (user) => {
    try {
      await fetch('/api/users', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(user),
      });
      fetchUsers(); // 重新获取
    } catch (err) {
      setError(err.message);
    }
  };
  
  // ... update, delete 也需类似代码
  
  useEffect(() => { fetchUsers(); }, []);
  
  return <>...</>;
}

// === SWR: ~80 lines ===
function UserCRUDWithSWR() {
  const { data: users, error, mutate } = useSWR('/api/users', fetcher);
  
  const createUser = async (user) => {
    await fetch('/api/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(user),
    });
    mutate(); // 自动重新验证
  };
  
  const updateUser = async (id, data) => {
    await fetch(`/api/users/${id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    mutate();
  };
  
  return <>...</>;
}

// === React Query: ~60 lines ===
function UserCRUDWithRQ() {
  const { data: users } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });
  
  const { mutate: createUser } = useMutation({
    mutationFn: createUserAPI,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });
  
  const { mutate: updateUser } = useMutation({
    mutationFn: ({ id, data }) => updateUserAPI(id, data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });
  
  return <>...</>;
}

// === GraphQL: ~40 lines ===
function UserCRUDWithGraphQL() {
  const { data } = useQuery(GET_USERS);
  const [createUser] = useMutation(CREATE_USER, {
    refetchQueries: [{ query: GET_USERS }],
  });
  const [updateUser] = useMutation(UPDATE_USER, {
    refetchQueries: [{ query: GET_USERS }],
  });
  
  return <>...</>;
}

// === tRPC: ~30 lines ===
function UserCRUDWithTRPC() {
  const { data: users } = trpc.user.getAll.useQuery();
  const { mutate: createUser } = trpc.user.create.useMutation({
    onSuccess: () => utils.user.getAll.invalidate(),
  });
  const { mutate: updateUser } = trpc.user.update.useMutation({
    onSuccess: () => utils.user.getAll.invalidate(),
  });
  
  return <>...</>;
}

类型安全对比

tsx
// 1. TypeScript支持程度

// === Fetch API: 弱类型 ===
async function fetchUser(id: string) {
  const response = await fetch(`/api/users/${id}`);
  const data = await response.json(); // type: any
  return data; // 需手动类型断言
}

// === Axios: 泛型支持 ===
async function fetchUser(id: string) {
  const { data } = await axios.get<User>(`/api/users/${id}`);
  return data; // type: User (手动指定)
}

// === React Query: 强类型 ===
function useUser(id: string) {
  return useQuery({
    queryKey: ['user', id],
    queryFn: async (): Promise<User> => {
      const { data } = await axios.get<User>(`/api/users/${id}`);
      return data;
    },
  });
  // data: User | undefined (自动推断)
}

// === GraphQL + Codegen: 极强 ===
// 自动生成类型
const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`;

function useUser(id: string) {
  const { data } = useQuery(GET_USER, { variables: { id } });
  // data: GetUserQuery (自动生成)
  // data.user.name - 完全类型安全
}

// === tRPC: 最强 ===
// 服务端定义
const userRouter = router({
  getUser: publicProcedure
    .input(z.object({ id: z.string() }))
    .query(({ input }) => db.user.findUnique({ where: { id: input.id } })),
});

// 客户端 - 完全自动
function useUser(id: string) {
  const { data } = trpc.user.getUser.useQuery({ id });
  // data: User (自动推断,无需任何类型注解)
  // 重构时自动同步
}

错误处理对比

tsx
// 1. 错误处理复杂度

// === Fetch: 最复杂 ===
async function fetchWithFetch() {
  try {
    const res = await fetch('/api/users');
    
    if (!res.ok) {
      if (res.status === 404) throw new Error('Not found');
      if (res.status === 401) throw new Error('Unauthorized');
      throw new Error('Request failed');
    }
    
    const data = await res.json();
    return data;
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Request aborted');
    } else if (error.name === 'TypeError') {
      console.log('Network error');
    }
    throw error;
  }
}

// === Axios: 简化 ===
axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response) {
      // 服务器响应错误
      const status = error.response.status;
      if (status === 401) handleUnauthorized();
      if (status === 404) handleNotFound();
    } else if (error.request) {
      // 请求发出但无响应
      handleNetworkError();
    } else {
      // 请求配置错误
      handleRequestError();
    }
    return Promise.reject(error);
  }
);

// === SWR: 集成错误状态 ===
function Component() {
  const { data, error, isLoading } = useSWR('/api/users', fetcher, {
    onError: (error) => {
      if (error.status === 401) redirect('/login');
    },
    onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
      if (error.status === 404) return; // 不重试404
      if (retryCount >= 3) return; // 最多3次
      setTimeout(() => revalidate({ retryCount }), 5000);
    },
  });
  
  if (error) return <ErrorComponent error={error} />;
  return <div>{data}</div>;
}

// === React Query: 最完善 ===
function Component() {
  const { data, error, isError } = useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
    retry: (failureCount, error) => {
      if (error.response?.status === 404) return false;
      return failureCount < 3;
    },
    retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
    onError: (error) => {
      toast.error(error.message);
    },
  });
  
  return <div>...</div>;
}

// === GraphQL: 结构化错误 ===
function Component() {
  const { data, error } = useQuery(GET_USERS);
  
  if (error) {
    // GraphQL错误是结构化的
    error.graphQLErrors.forEach((err) => {
      if (err.extensions?.code === 'UNAUTHENTICATED') {
        redirect('/login');
      }
    });
    
    if (error.networkError) {
      handleNetworkError();
    }
  }
  
  return <div>...</div>;
}

// === tRPC: 类型安全错误 ===
function Component() {
  const { data, error } = trpc.user.getAll.useQuery();
  
  if (error) {
    // 错误也是类型安全的
    if (error.data?.code === 'UNAUTHORIZED') {
      redirect('/login');
    }
    
    // 错误消息也有类型
    toast.error(error.message);
  }
  
  return <div>...</div>;
}

缓存策略对比

tsx
// 1. 缓存机制比较

// === SWR: Stale-While-Revalidate ===
const { data } = useSWR('/api/users', fetcher, {
  // 立即返回缓存(stale),后台重新验证
  revalidateOnFocus: true,
  revalidateOnReconnect: true,
  dedupingInterval: 2000,
});
/*
优点: 
- 极快的用户体验
- 自动保持数据新鲜
- 去重优化
缺点:
- 可能显示过期数据
- 网络请求较多
*/

// === React Query: 可配置缓存 ===
const { data } = useQuery({
  queryKey: ['users'],
  queryFn: fetchUsers,
  staleTime: 5 * 60 * 1000,  // 5分钟内不重新获取
  cacheTime: 10 * 60 * 1000, // 10分钟后清除缓存
  refetchOnMount: 'always',
  refetchOnWindowFocus: false,
});
/*
优点:
- 高度可配置
- 精确控制缓存行为
- 性能优化
缺点:
- 配置复杂
- 需要理解多个概念
*/

// === GraphQL: 归一化缓存 ===
const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        users: {
          merge(existing = [], incoming) {
            return [...existing, ...incoming];
          },
        },
      },
    },
  },
});

const { data } = useQuery(GET_USERS);
/*
优点:
- 归一化存储,避免重复
- 自动更新相关查询
- 内存效率高
缺点:
- 配置复杂
- 学习曲线陡峭
- 调试困难
*/

// === tRPC: 基于React Query ===
const { data } = trpc.user.getAll.useQuery(undefined, {
  staleTime: 5000,
  cacheTime: 10000,
});
/*
优点:
- 继承React Query优点
- 类型安全
- 简单易用
缺点:
- 依赖React Query
- 功能受限于RQ
*/

实时数据对比

tsx
// 1. 实时更新能力

// === SWR: 轮询 + 手动刷新 ===
const { data, mutate } = useSWR('/api/stats', fetcher, {
  refreshInterval: 3000, // 3秒轮询
});

// WebSocket集成需手动处理
useEffect(() => {
  const ws = new WebSocket('ws://localhost:3000');
  ws.onmessage = (event) => {
    mutate(JSON.parse(event.data), false);
  };
  return () => ws.close();
}, [mutate]);

// === React Query: 轮询 + 手动失效 ===
const { data } = useQuery({
  queryKey: ['stats'],
  queryFn: fetchStats,
  refetchInterval: 3000,
});

// WebSocket集成
useEffect(() => {
  const ws = new WebSocket('ws://localhost:3000');
  ws.onmessage = () => {
    queryClient.invalidateQueries({ queryKey: ['stats'] });
  };
  return () => ws.close();
}, []);

// === GraphQL: 原生订阅 ===
const STATS_SUBSCRIPTION = gql`
  subscription OnStatsUpdated {
    statsUpdated {
      value
      timestamp
    }
  }
`;

function Component() {
  const { data } = useSubscription(STATS_SUBSCRIPTION);
  // 自动接收实时更新
  return <div>{data?.statsUpdated.value}</div>;
}

// === tRPC: WebSocket订阅 ===
function Component() {
  const [stats, setStats] = useState<Stats[]>([]);
  
  trpc.stats.onUpdate.useSubscription(undefined, {
    onData: (data) => {
      setStats(prev => [...prev, data]);
    },
  });
  
  return <div>{stats.map(s => s.value)}</div>;
}

/*
实时能力排名:
1. GraphQL - 原生支持,最完善
2. tRPC - WebSocket集成,类型安全
3. SWR - 轮询 + 手动集成
4. React Query - 轮询 + 手动集成
5. Fetch/Axios - 完全手动
*/

迁移成本对比

tsx
// 1. 从Fetch迁移到各方案的难度

// === 迁移到Axios: 容易 (1-2天) ===
// Before
const response = await fetch('/api/users');
const users = await response.json();

// After
const { data: users } = await axios.get('/api/users');

// === 迁移到SWR: 中等 (3-5天) ===
// Before
const [users, setUsers] = useState([]);
useEffect(() => {
  fetch('/api/users').then(r => r.json()).then(setUsers);
}, []);

// After
const { data: users } = useSWR('/api/users', fetcher);

// === 迁移到React Query: 中等 (5-7天) ===
// Before
const [users, setUsers] = useState([]);
useEffect(() => {
  fetch('/api/users').then(r => r.json()).then(setUsers);
}, []);

// After
const { data: users } = useQuery({
  queryKey: ['users'],
  queryFn: fetchUsers,
});

// === 迁移到GraphQL: 困难 (2-4周) ===
// 需要:
// 1. 设置GraphQL服务器
// 2. 定义Schema
// 3. 编写Resolvers
// 4. 配置Apollo Client
// 5. 重写所有查询

// === 迁移到tRPC: 中等-困难 (1-3周) ===
// 需要:
// 1. TypeScript项目
// 2. 设置tRPC服务器
// 3. 定义Router
// 4. 配置客户端
// 5. 重写API层

/*
迁移成本排名(从低到高):
1. Axios - 最低,几乎直接替换
2. SWR - 较低,改变数据获取模式
3. React Query - 中等,学习概念
4. tRPC - 较高,需要全栈TypeScript
5. GraphQL - 最高,需要重新设计API
*/

生态系统对比

tsx
// 1. 工具和插件

// === React Query生态 ===
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client';
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister';

// 插件:
// - DevTools (调试)
// - Persist (持久化)
// - Broadcast (跨标签同步)
// - Mock (测试)

// === GraphQL生态 ===
import { ApolloClient } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { onError } from '@apollo/client/link/error';
import { RetryLink } from '@apollo/client/link/retry';
import { persistCache } from 'apollo3-cache-persist';

// 工具:
// - Apollo DevTools
// - GraphQL Playground
// - GraphQL Codegen
// - Apollo Studio
// - GraphQL Voyager

// === tRPC生态 ===
import { createTRPCReact } from '@trpc/react-query';
import { httpBatchLink } from '@trpc/client';
import superjson from 'superjson';

// 集成:
// - Next.js
// - Express
// - Fastify
// - CloudFlare Workers

// === SWR生态 ===
import useSWR from 'swr';
import useSWRInfinite from 'swr/infinite';
import useSWRMutation from 'swr/mutation';

// 较小,主要是核心库

/*
生态系统排名:
1. GraphQL - 最丰富,工具最多
2. React Query - 完善,插件多
3. Axios - 成熟,社区大
4. tRPC - 新兴,快速增长
5. SWR - 较小,专注核心
*/

实际项目案例

案例1: 电商平台

tsx
/*
项目: 大型电商平台
需求: 
- 复杂的数据关系(商品、订单、用户)
- 高性能要求
- 移动端优化
- 实时库存更新

选择: GraphQL + Apollo Client

理由:
✅ 精确获取数据,减少传输
✅ 归一化缓存,避免重复
✅ 实时订阅支持
✅ 适合复杂数据关系

结果:
- API请求减少60%
- 数据传输减少40%
- 首屏加载提升30%
*/

// 实现示例
const PRODUCT_QUERY = gql`
  query GetProduct($id: ID!) {
    product(id: $id) {
      id
      name
      price
      inventory
      reviews(first: 5) {
        rating
        comment
      }
      relatedProducts {
        id
        name
        price
      }
    }
  }
`;

function ProductPage({ id }) {
  const { data } = useQuery(PRODUCT_QUERY, {
    variables: { id },
  });
  
  const [subscribe] = useSubscription(INVENTORY_SUBSCRIPTION, {
    variables: { productId: id },
    onData: ({ data }) => {
      // 实时更新库存
      cache.modify({
        id: cache.identify({ __typename: 'Product', id }),
        fields: {
          inventory: () => data.inventoryUpdated.quantity,
        },
      });
    },
  });
  
  return <ProductDetail product={data?.product} />;
}

案例2: 内部管理系统

tsx
/*
项目: 企业内部管理系统
需求:
- 快速开发
- 类型安全
- 简单维护
- 团队都用TypeScript

选择: tRPC + React Query

理由:
✅ 端到端类型安全
✅ 无需Schema定义
✅ 开发效率高
✅ 重构友好

结果:
- 开发速度提升50%
- 运行时错误减少80%
- 代码量减少40%
*/

// 实现示例
// 服务端
const appRouter = router({
  user: router({
    list: publicProcedure
      .input(z.object({
        page: z.number(),
        search: z.string().optional(),
      }))
      .query(async ({ input }) => {
        return db.user.findMany({
          where: input.search ? {
            name: { contains: input.search },
          } : undefined,
          skip: input.page * 20,
          take: 20,
        });
      }),
    
    create: protectedProcedure
      .input(z.object({
        name: z.string(),
        email: z.string().email(),
      }))
      .mutation(async ({ input }) => {
        return db.user.create({ data: input });
      }),
  }),
});

// 客户端
function UserManagement() {
  const [page, setPage] = useState(0);
  const [search, setSearch] = useState('');
  
  const { data: users } = trpc.user.list.useQuery({ page, search });
  const { mutate: createUser } = trpc.user.create.useMutation({
    onSuccess: () => {
      utils.user.list.invalidate();
    },
  });
  
  // 完全类型安全,自动补全
  return <div>...</div>;
}

案例3: 新闻资讯应用

tsx
/*
项目: 新闻资讯移动应用
需求:
- 实时数据更新
- 离线阅读
- 包体积小
- 简单易用

选择: SWR

理由:
✅ 轻量级(~5KB)
✅ 自动重新验证
✅ 开箱即用
✅ 学习成本低

结果:
- 包体积最小化
- 用户体验流畅
- 开发周期短
*/

// 实现示例
function NewsList() {
  const { data: news } = useSWR('/api/news', fetcher, {
    refreshInterval: 30000, // 30秒刷新
    revalidateOnFocus: true,
    revalidateOnReconnect: true,
  });
  
  const { data: trending } = useSWR('/api/trending', fetcher, {
    refreshInterval: 60000,
  });
  
  return (
    <div>
      <TrendingSection data={trending} />
      <NewsSection data={news} />
    </div>
  );
}

// 离线支持
const { data } = useSWR('/api/news', fetcher, {
  fallbackData: cachedNews, // 离线时使用缓存
  onSuccess: (data) => {
    localStorage.setItem('news', JSON.stringify(data));
  },
});

案例4: 数据分析仪表板

tsx
/*
项目: 实时数据分析仪表板
需求:
- 多数据源
- 复杂缓存策略
- 分页和无限滚动
- 性能监控

选择: TanStack Query

理由:
✅ 功能最完整
✅ 强大的缓存管理
✅ 优秀的DevTools
✅ 灵活配置

结果:
- 缓存命中率90%
- 请求优化70%
- 用户体验提升
*/

// 实现示例
function Dashboard() {
  // 并行查询多个数据源
  const queries = useQueries({
    queries: [
      { queryKey: ['sales'], queryFn: fetchSales, staleTime: 60000 },
      { queryKey: ['users'], queryFn: fetchUsers, staleTime: 30000 },
      { queryKey: ['orders'], queryFn: fetchOrders, staleTime: 10000 },
    ],
  });
  
  // 无限滚动
  const {
    data,
    fetchNextPage,
    hasNextPage,
  } = useInfiniteQuery({
    queryKey: ['transactions'],
    queryFn: ({ pageParam = 0 }) => fetchTransactions(pageParam),
    getNextPageParam: (lastPage) => lastPage.nextCursor,
  });
  
  // 依赖查询
  const userId = queries[1].data?.currentUserId;
  const { data: userDetails } = useQuery({
    queryKey: ['userDetails', userId],
    queryFn: () => fetchUserDetails(userId),
    enabled: !!userId,
  });
  
  return <div>...</div>;
}

最终建议矩阵

场景                  推荐方案              备选方案
─────────────────────────────────────────────────
小型项目(<5页面)      Fetch + Hook        SWR
中型项目(5-20页面)    SWR                React Query
大型项目(>20页面)     React Query        tRPC/GraphQL
企业应用              tRPC               React Query
电商平台              GraphQL            React Query
移动应用              GraphQL/SWR        React Query
实时应用              GraphQL            tRPC
内部工具              tRPC               React Query
新项目                tRPC               React Query
遗留项目迁移          Axios              SWR

团队技能              推荐方案              备选方案
─────────────────────────────────────────────────
JavaScript only      SWR                Axios
TypeScript          tRPC               React Query
GraphQL熟悉         GraphQL            -
新手团队            SWR                Fetch
经验团队            tRPC/GraphQL       React Query

性能要求              推荐方案              备选方案
─────────────────────────────────────────────────
极致性能            SWR                React Query
包体积最小          tRPC               Fetch
首屏速度            SWR                Fetch
运行时性能          React Query        SWR
内存占用最小        Fetch              tRPC

总结与展望

当前状态(2024)

  1. React Query/TanStack Query: 最受欢迎,功能最全
  2. tRPC: 快速增长,TypeScript项目首选
  3. GraphQL: 成熟稳定,大型项目标配
  4. SWR: 轻量简洁,小项目利器
  5. Fetch/Axios: 基础工具,永不过时

未来趋势

  1. 类型安全: 端到端类型安全成为标准
  2. 性能优化: 自动化性能优化和监控
  3. 离线优先: 更好的离线支持和同步
  4. AI集成: 智能预取和缓存策略
  5. 边缘计算: 边缘渲染和数据获取

选择清单

在选择数据获取方案时,考虑以下因素:

项目规模: 小/中/大 ✅ 团队技能: JS/TS/GraphQL ✅ 性能要求: 首屏/运行时/内存 ✅ 类型安全: 弱/强/极强 ✅ 开发效率: 快速/稳健/长期 ✅ 维护成本: 学习曲线/文档/社区 ✅ 迁移风险: 锁定/灵活/可替换

记住: 没有银弹,只有权衡。选择最适合你项目的方案,而不是最流行的方案。