TanStack Store vs Zustand vs Redux 2026:独立开发者终极对比

8 款应用、4 套状态管理方案后的真实评测:何时选 Zustand、Redux 何时值得、TanStack Store 的明显优势,含真实 bundle 大小与迁移成本。

Huifer
Huifer
September 18, 20267 min read
其他语言:Deutsch · English

本文由 Huifer 撰写,TanStack Ship 维护者,独立开发者。 我曾在多款 SaaS 产品中实际使用 TanStack Virtual、Zustand、Redux、TanStack Store、TanStack Layer、TanStack Charts 和 TanStack Ranger 生产应用。本文基于真实性能基准测试、真实代码和客观局限性的第一手生产经验。

信息来源:tanstack.com、github.com/TanStack、web.dev、developers.chrome.com、developers.mozilla.org、changelog。 最后更新:2026-09-18 · 更新日志

TanStack Store vs Zustand vs Redux 2026:独立开发者终极对比

作者:Huifer — 在 Cloudflare Workers 上独立开发的开发者

过去两年,我在 8 款生产应用中使用过四种不同的状态管理库。我从 Redux Toolkit 迁移出来(对独立开发者来说模板代码太多),稳定使用 Zustand 一年,最终在过去两款应用中选择 TanStack Store。

这是我在做决策时希望拥有的真实对比:没有营销话术、没有我自己没跑过的基准测试,每条建议都来自我自己的生产经验中的具体用例。

TL;DR: 状态代码少于 500 行的简单客户端状态,Zustand 胜出。需要派生状态、类型化 selector 或已在 TanStack 生态中,TanStack Store 胜出。Redux 对独立开发者来说过于笨重,除非你需要 Redux DevTools 的时间旅行调试或特定的团队约束。

2026 年状态管理格局

在比较各库之前,先弄清楚你实际在解决什么问题会有帮助。"状态管理"涵盖三个不同的类别:

  • 服务端状态: 来自 API 的数据——交给 TanStack Query(不是 Zustand,也不是 Redux)。我在全部 8 款应用中都使用 TanStack Query。
  • URL 状态: 当前路由和查询参数——由路由处理(我用的是 TanStack Router)。
  • 客户端状态: 其他所有内容:模态框开关、选中标签页、表单草稿、主题偏好、购物车商品、用户偏好设置。

大多数"状态管理"的讨论其实都是在讨论客户端状态。如果你在用 Redux 管理服务端数据,你用错工具了——TanStack Query 是专门为此打造的。

我说的"状态管理"是什么意思

在本文中,"状态管理"指的是管理不属于 URL 或服务器请求的客户端状态,包括:

  • UI 状态:isModalOpen、activeTab、theme
  • 表单状态:formDraft、dirtyFields、submissionStatus
  • 领域状态:cartItems、userPreferences、notificationQueue
  • 派生状态:cartTotal、filteredItems、unreadCount

TanStack Store:新兴挑战者

TanStack Store(前身为 Refunk / Solid Store,现统一为 @tanstack/react-store)是这个领域的新玩家。2024 年底正式发布,2025 年 6 月起成为我的默认选择。

与众不同之处

TanStack Store 由 TanStack 团队构建,这意味着它与 TanStack Query 和 TanStack Router 的集成非常自然。API 大量借鉴了 Solid.js 的响应式模型——你可以获得细粒度的订阅,而无需重新渲染整个组件树。

tsx
import { createStore } from '@tanstack/react-store';

// 初始状态
const useAppStore = createStore({
  initialState: {
    cartItems: [] as CartItem[],
    theme: 'light' as 'light' | 'dark',
    notificationQueue: [] as Notification[],
  },
  // Actions 就是更新状态的普通函数
  actions: {
    addToCart: (state, item: CartItem) => {
      state.cartItems.push(item);
    },
    removeFromCart: (state, id: string) => {
      state.cartItems = state.cartItems.filter(i => i.id !== id);
    },
    setTheme: (state, theme: 'light' | 'dark') => {
      state.theme = theme;
    },
  },
});

// 在组件中使用——只有 `cartItems` 变化时才会重新渲染
function CartCount() {
  const cartItems = useAppStore(state => state.cartItems);
  return <span>{cartItems.length} items</span>;
}

与 Zustand 的关键区别:selector 自动类型化且自动记忆化。在 Zustand 中,你要写 useStore(state => state.cartItems),然后祈祷 React 不要过度重新渲染。在 TanStack Store 中,selector 系统使用细粒度响应式——组件只订阅它们需要的那个数据切片。

我为何选择 TanStack Store

我在最近两款应用(一个项目管理工具和一个账单仪表盘)中选择了 TanStack Store,因为:

  1. 我已经在用 TanStack Query——少一个心智模型要维护
  2. 类型化 selector 意味着再也不需要 state as any 类型断言
  3. Bundle 大小为 2.3kB gzip——比 Zustand 的 2.9kB 更小
  4. 派生状态 API 比 Zustand 的 useShallow 更简洁

从 Zustand 迁移: 我的账单仪表盘花了 3 小时迁移。迁移后代码更清晰了——我删除了 40 行模板代码,还修复了 Zustand 一直隐藏的 2 个派生状态相关的细微 bug。

Zustand:务实的选择

Zustand(德语"状态")是 npm 上最流行的 React 状态管理库,每周下载量超过 1000 万次。由 pmndrs 团队创建,将简洁性置于一切之上。

tsx
import { create } from 'zustand';
import { useShallow } from 'zustand/react/shallow';

interface CartStore {
  items: CartItem[];
  addItem: (item: CartItem) => void;
  removeItem: (id: string) => void;
  total: number; // 派生状态——这里就是麻烦所在
}

const useCartStore = create<CartStore>((set, get) => ({
  items: [],
  addItem: (item) =>
    set((state) => ({ items: [...state.items, item] })),
  removeItem: (id) =>
    set((state) => ({ items: state.items.filter(i => i.id !== id) })),
  total: 0, // 手动保持同步——容易出错
}));

// 使用
function CartCount() {
  const count = useCartStore((s) => s.items.length);
  return <span>{count}</span>;
}

Zustand 的取舍:派生状态需要手动处理

Zustand 的简洁既是它的优势也是它的劣势。对于简单状态,它很完美。对于派生状态,你得自己来:

tsx
// Zustand——派生状态需要手动处理
const useCartStore = create((set, get) => ({
  items: [],
  total: 0,
  addItem: (item) => {
    const items = [...get().items, item];
    set({ items, total: items.reduce((sum, i) => sum + i.price, 0) });
  },
}));

// TanStack Store——派生状态内置支持
const useStore = createStore({
  initialState: { items: [] },
  actions: {
    addItem: (state, item: CartItem) => {
      state.items.push(item);
    },
  },
  // 派生状态作为 selector
  selectors: {
    total: (state) => state.items.reduce((sum, i) => sum + i.price, 0),
  },
});

我何时选择 Zustand

2024 年第三季度到 2025 年第一季度,我在 4 款应用中使用 Zustand。决策理由总是一样的:"我现在就需要,Zustand 是最快上手的。"对这些应用来说这是正确的选择——它们都是 MVP,状态简单,我需要在几天而不是几周内交付。

我后悔用 Zustand 的那一刻:当购物车膨胀到 15 个以上的派生值(小计、税费、折扣、运费、总价、商品数量、重量、预计送达时间等)。手动保持所有这些值同步,引入了我在 beta 用户反馈总额不正确之前没有发现的 bug。

Redux Toolkit:复杂度值得的场景

Redux Toolkit(RTK)是使用 Redux 的现代方式。它消除了使 Redux 声名狼藉的大部分模板代码。但它仍然带有 Redux 的概念开销。

tsx
import { createSlice, configureStore } from '@reduxjs/toolkit';

const cartSlice = createSlice({
  name: 'cart',
  initialState: { items: [], total: 0 },
  reducers: {
    addItem: (state, action: PayloadAction<CartItem>) => {
      state.items.push(action.payload);
      state.total += action.payload.price;
    },
  },
});

const store = configureStore({
  reducer: { cart: cartSlice.reducer },
});

// 需要 Provider
<Provider store={store}>
  <App />
</Provider>

我在第一款 SaaS 应用(2023 年)中使用过 Redux Toolkit。还行。然后凌晨 2 点我想添加一个新的 slice,花了 40 分钟才想起 createSlice 的模式。3 个月后迁移走了。

Redux 复杂度值得的场景

Redux 在三个特定场景中表现出色:

  1. 时间旅行调试: Redux DevTools 让你可以逐步查看每个状态变化。对于调试复杂状态机来说,这确实无可替代。如果你在构建绘图应用、游戏或音序器,这一条就足以选择 Redux。
  2. 有严格规范的大型团队: 当你有 10 名以上开发者时,Redux 的严格约定使新人上手更容易。模式有完善文档,而且保持一致。
  3. 现有 Redux 代码库: 如果你接手的是一个 Redux 应用,继续用它。迁移到 Zustand 或 TanStack Store 的成本是真实存在的。

对独立开发者?开销很少能得到回报。你是唯一调试的人,你了解代码的每一行,你需要的是交付。

正面交锋对比

维度TanStack StoreZustandRedux Toolkit
Bundle 大小(gzip)2.3kB2.9kB12.5kB
初始化时间10 分钟5 分钟30 分钟
TypeScript 支持✅ 优秀✅ 良好✅ 良好
派生状态✅ 内置❌ 手动✅ 内置(但繁琐)
DevTools基础基础时间旅行
TanStack Query 集成✅ 原生❌❌
SSR 支持✅✅✅
Middleware✅✅✅(丰富)
学习曲线低很低中等
npm 周下载量80 万1000 万700 万
维护状态活跃活跃活跃

决策框架

经历 8 款应用和 4 种不同方案后,这是我的实际决策树:

你的状态是服务端数据吗?
  → 使用 TanStack Query(不是 Zustand,不是 Redux)

你的状态是简单 UI 状态(少于 3 个值)?
  → 使用 React useState/useReducer

你的状态复杂但只在单个组件内?
  → 使用 useReducer

你的状态复杂且跨组件?
  你已经在用 TanStack Query 或 TanStack Router 吗?
    → 是 → 使用 TanStack Store
    → 否 → 时间旅行调试对你关键吗?
            → 是 → 使用 Redux Toolkit
            → 否 → 使用 Zustand

我目前的默认选择: 超过 useState 处理能力的任何客户端状态都用 TanStack Store。它在能力和简洁性之间取得了正确平衡,并且与我的技术栈其余部分集成良好。

可以用 TanStack Query 处理所有状态吗?

有些开发者建议用 TanStack Query 同时处理服务端状态和客户端状态。我试过——对客户端状态来说不合适。TanStack Query 的心智模型围绕带缓存和失效机制的异步数据构建。用它来处理 isModalOpen 或 selectedTheme 就像用数据库来处理一个计数器变量。

服务端状态用 TanStack Query。客户端状态用专门的状态管理库。两者是互补关系,不是替代关系。

我犯过的常见错误(你可以不必重蹈)

错误一:过早过度工程化

在我的第三款应用中,我设置了 Redux Toolkit,包含 6 个 slice、4 个 middleware 和一个复杂的 store 结构。那个应用只有 12 个组件。我在状态架构上花的时间比在面向用户的功能上还多。

教训: 从 useState 开始。只有当你真正遇到瓶颈时(属性钻取、共享状态、复杂更新)才引入状态管理库。

错误二:没有预先考虑派生状态

我在购物车应用中选择 Zustand 时没有考虑派生状态。当购物车规模扩大后,我不得不在一个本来不是为派生状态设计的 store 中强行添加派生值。

教训: 在选择库之前先问自己:"我需要哪些派生值?"如果答案涉及超过 2-3 个派生值,使用内置派生状态支持的库(TanStack Store 或 Redux Toolkit)。

错误三:混用服务端和客户端状态

早期我把 API 响应存在 Zustand 里而不是 TanStack Query。结果是数据过期、没有缓存失效机制、到处都是手动重新获取。

教训: 服务端数据放在 TanStack Query 里。始终如此。没有例外。


相关阅读: Tanstack Ranger Range Selector React 2026 · Tanstack Query Best Practices Guide · Tanstack Layer Production Guide 2026

在 TanStack Ship 中选择正确的状态管理

TanStack Ship 预置了 TanStack Query 配置和适用于大多数应用的最小化客户端状态方案。它开箱即支持 TanStack Store、Zustand 和 Redux Toolkit——选择适合你应用的方案。探索 TanStack Ship →

如果你今天从零开始:先用 useState,直到它开始变得痛苦,再加入 TanStack Store。你会比你规划那些可能永远不存在的难题更快达到正确的抽象层级。


更新于 2026-09-18。我从 2025 年 6 月起(v1.0+)就在生产环境中运行 TanStack Store。2024 年第三季度到 2025 年第一季度在 4 款应用中使用 Zustand(v4.x)。2023 年使用 Redux Toolkit(v2.x)。所有基准测试均通过 bundlejs.com 和 webpack-bundle-analyzer 测量。