• AaronConlon.dev
  • Talk
  • Weekly
  • 工具
  • 编程
  • 翻译
  • 资讯
  • 通识

TanStack 的崛起(下):竞品、Issue 与未来野心

2026年07月20上次更新于 大约 2 个月前
编程

hello!大家好!

上篇我们从 React Query 出发,走到了 TanStack 的产品矩阵。本篇继续往下看:当它面对 SWR、RTK Query、Apollo、React Router 和 Next.js 等方案时,究竟把复杂度放在了哪里;公开 Issue 又如何暴露它的优势与代价。

和前端竞品放在一起看

比较工具时,最容易陷入“谁的 API 更漂亮”,但这种看法非常主观,千人千面,很多时候谁也说服不了谁。更实用的方式是看它们把复杂度放在哪里。

数据请求:SWR、RTK Query、Apollo 与 TanStack Query

SWR 的优势是轻量和直接,适合希望用较少抽象实现缓存与重新验证的项目。它的心智模型清楚,和 React 组件组合自然。TanStack Query 的范围更大,除了查询缓存,还把 mutation 生命周期、离线、持久化、开发工具和多框架适配纳入体系。项目越复杂,后者的统一性越有价值;项目越简单,SWR 的轻量可能更舒服。

RTK Query 与 Redux Toolkit 绑定得更紧。如果团队已经把 Redux 作为全局状态中心,RTK Query 可以让接口数据进入同一套 store、middleware 和规范中。TanStack Query 则坚持把服务器状态从客户端 store 中分离出来。两者没有绝对高下,区别在于团队是否希望所有状态共享一套 Redux 基础设施。

Apollo Client 解决的是更明确的 GraphQL 场景:它会围绕 schema、实体缓存和查询文档建立一整套客户端模型。拿它直接和 TanStack Query 比,容易忽略数据协议的差异。TanStack Query 更像通用的异步状态层,Apollo 则更像 GraphQL 应用的专用客户端。

路由与全栈:React Router、Next.js 与 TanStack Router

React Router 的优势是生态广、历史长、团队认知成本低,并且已经覆盖数据路由与框架模式。TanStack Router 的差异在于类型安全更彻底:路由树、路径参数、搜索参数和 loader 数据之间的关系,尽量在编译阶段暴露问题。

Next.js App Router 则是更完整的应用框架,服务端组件、部署平台和约定式工程能力都更成熟。TanStack Router 与 Start 选择的是更可组合、更靠近 Vite 和运行时的路线。前者适合希望快速获得完整平台的团队,后者适合希望保留更多基础设施选择权、并把路由和数据流作为核心设计的团队。

这个选择也对应 TanStack 的长期风险:类型安全与可组合性越强,学习曲线和类型复杂度越高;框架越年轻,生态和文档稳定性就越需要时间验证。

表格:AG Grid、MUI Data Grid 与 TanStack Table

AG Grid 和 MUI Data Grid 更强调完整组件体验,适合希望快速得到排序、筛选、分页、编辑和主题能力的团队。TanStack Table 把渲染层留给应用,因此能适配更多设计系统,但也把无障碍、键盘行为、空状态和性能细节交还给开发者。

换句话说,TanStack Table 的“自由”不是免费的。它的价值不在于让所有项目都少写代码,而在于当产品需要特殊的交互和视觉约束时,底层引擎不会成为天花板。

小黑比较不同工具的适用边界

从公开 Issue 看团队如何处理问题

开源项目真正的工程能力,往往要到问题发生时才看得出来。TanStack 的 GitHub Issue 有一个很明显的特点:维护者经常不急着把所有反馈都定义成 bug,而是先解释行为背后的模型,再给出迁移方式、临时方案或明确的“不计划支持”。这让讨论不一定总是让提问者满意,但通常能留下可复用的上下文。

Query 的 hydration 争议:正确性优先于旧行为

在 Issue #6240 中,用户反馈 v5 的 HydrationBoundary 和 v4 的 Hydrate 行为不同:客户端切换页面时,缓存数据似乎更新得太晚,页面会先使用旧数据渲染。

维护者没有简单把它当作回归修掉,而是解释了这个变化的原因:v4 可能在 React transition 完成前就修改当前页面的缓存,甚至在 transition 被取消后留下可观察的副作用;v5 选择把 hydration 推迟到渲染之后,接受短暂的不完美,以避免在 render 阶段修改外部缓存。对于直接读取缓存但没有建立 observer 的用法,维护者也明确指出这不是推荐模式,并建议使用 useQuery 或自行保留底层 hydrate 方案。

这个案例的价值不在于 v5 一定没有代价,而在于它展示了一种取舍:当 React 的并发模型与旧缓存行为冲突时,团队选择了更容易解释的正确性,并给高级用户留下低层 API。这样的答复比“升级到最新版即可”更有长期价值,但也意味着迁移者需要真正理解数据订阅模型。

小黑守住 hydration 的渲染闸门

浏览器兼容:支持范围就是产品边界

另一个更直接的例子是 Issue #6371。用户在 Safari 14 中升级 Query v5 后遇到私有类字段语法错误。维护者的判断很明确:v5 的支持范围从 Safari 15 开始,Safari 14 不属于默认支持的浏览器;如果项目必须兼容旧浏览器,就让构建工具转译依赖,或者暂时停留在 v4。

这不是一个讨喜的答案,却是一种清晰的工程沟通。团队没有为了覆盖所有环境而牺牲包体积和现代语法,也没有把“浏览器支持”隐藏在实现细节里。开源库的使用者因此可以根据目标用户做选择,而不是在生产环境里才发现隐含条件。

小黑检查浏览器支持门槛

类型变更:修复很快,但兼容策略仍有争议

在 Issue #9660 中,v5.89.0 的 mutation 回调类型发生变化,onSuccess、onError 和 onSettled 的参数位置与类型让一些已有代码无法编译。维护者先解释新增参数与已有上下文的关系,随后提交修复,并继续追问低层封装如何同时兼容不同版本。

小黑给新类型合同接上迁移插头

这个案例比前两个更能说明 TypeScript 库的现实:运行时没有变化,不代表类型层没有破坏性影响。TanStack 文档把类型修复视为可以随补丁版本发布的变化,这有助于持续改善类型推导,但对构建二次封装的团队并不总是轻松。它的优势是维护者能够快速响应类型问题,代价是升级时需要锁定补丁版本并认真阅读变更。

Router 与 Start:快速迭代带来的基础设施风险

Router 生态中的 Issue #3367 讨论了近期版本的非流式 SSR hydration 问题,Issue 最后指向已有 Discussion,而不是在问题页面里继续堆叠一个孤立修复。这种处理方式体现了项目在把具体故障放回更大的 SSR 讨论中,但对只想得到“哪个版本能用”的使用者来说,信息分散也会增加查找成本。

Start 的问题则更容易触及运行时边界。比如 Issue #7402 记录了持续 SSR 负载下的内存增长:报告者通过压测区分出 QueryClient 的 gcTime 保留和原生流缓冲两个路径,其中一个可以通过 gcTime: 0 缓解,另一个仍需要进一步处理。这个 Issue 后来关闭,但正文保留了复现条件、内存数据和 bisect 结果。

这里能看出 TanStack 和成熟大框架的差异。TanStack 更常把问题拆成可验证的技术假设,鼓励用户提供最小复现和测量结果;它不一定立刻给出一个封装好的答案,但会让问题边界变得清楚。对于早期的全栈框架,这种公开的工程过程很有价值,也提醒使用者:RC 产品不应该被当成已经完成所有生产验证的基础设施。

Table 的重渲染:headless 不等于自动优化

在 Issue #4794 中,用户反馈可编辑表格会出现大量行和单元格重渲染。讨论里出现了 useMemo、拆分并记忆 Row/Cell、避免不稳定的渲染函数等多种方案,后续评论还指出某些 flexRender 用法会导致组件被卸载再挂载。

这件事不能简单归咎于库“性能不好”。TanStack Table 把渲染层交给应用,应用也就必须负责稳定列定义、稳定数据引用、行级记忆和虚拟化策略。headless 的优势是没有固定组件层,代价是性能模型需要由使用者掌握。它适合愿意理解渲染链路的团队,不适合把所有细节都交给组件库的项目。

小黑排查公开 Issue

TanStack 为什么能迅速发展

把这些产品与 Issue 放在一起看,TanStack 的增长并不只是因为某个库碰巧流行,而是因为它形成了几条相互强化的路径。

一套连续的心智模型

Query 用查询键描述数据身份,Router 用路由树描述页面边界,Table 用状态模型描述数据视图。它们都在做同一件事:把隐含在组件和副作用里的关系,变成可以组合、可以观察、可以被类型系统检查的结构。

开发者从 Query 进入后,很容易理解 Router 的 loader、Table 的受控状态和 Virtual 的可见范围。产品之间不必完全绑定,但学习收益可以迁移,这比单纯增加包数量更重要。

Headless 与可移植性

TanStack 不急于提供一套统一视觉组件,而是把核心行为和渲染层拆开。这让它能适配不同框架、设计系统和运行时,也减少了“换一个 UI 库就要重写业务逻辑”的成本。

当然,headless 也会把实现责任交还给应用。TanStack 的强项不是替你做完所有事情,而是让底层模型足够清楚,允许你在不同产品约束下继续组合。

TypeScript 不只是类型提示

在 Router 里,类型系统是路由合同;在 Query 里,它约束查询键、函数返回值和 mutation 参数;在 Table 和 Form 里,它帮助字段、列和表单状态保持一致。类型因此不再只是编辑器里的辅助信息,而成为库设计的一部分。

小黑检查类型合同

这也是 TanStack 与一些“运行起来就行”的工具的差异。它愿意把复杂度提前放进类型系统,换取大型项目在重构时更早暴露问题。代价是类型错误可能很长,升级时也更容易遇到补丁版本带来的编译变化。

公开的产品节奏

官网把不同产品标注为 stable、RC、beta 或 alpha,GitHub Issue 里也经常保留设计背景、复现步骤和维护者判断。它没有消除风险,但让风险更可见。

这套节奏还带来一个社区效应:使用者不只是等待一个封闭产品交付,而是可以通过 Issue、Discussion、赞助、文档和示例参与产品形成。对于一个以开源为核心的团队,这种持续的公开反馈比一次性宣传更能积累信任。

小黑把共同原则拼成工作台

TanStack 的野心与展望

TanStack 现在的产品矩阵,已经从“几个好用的 React 库”走向“应用开发中常见边界的一组基础设施”:数据获取、路由、全栈运行、表格表单、性能、开发工具,以及 AI agent 的使用方式。

这种野心有三个方向。

第一,它想把应用状态、URL 状态和服务器状态放进一套可以相互连接的模型里。Query 负责数据缓存,Router 负责页面与 URL,Start 负责服务器边界,三者组合后,应用可以在预取、渲染、流式输出和客户端更新之间减少重复胶水代码。

第二,它想保持基础设施的可替换性。与绑定某个部署平台或 UI 体系相比,核心库、适配器和运行时输出的分层更适合多框架、多运行时的前端世界。这也是它持续强调 framework agnostic、type-safe 和 no vendor lock-in 的原因。

第三,它开始为 agent 时代准备工具链。CLI 和 Intent 目前还不能与 Query、Table 的成熟度相提并论,但它们表达了一个判断:未来的开发者体验,不只是让人更快写代码,也要让自动化工具能理解项目结构、安装能力、遵循约束并完成验证。

不过,TanStack 也必须面对自己的扩张风险。产品越多,版本和文档之间的组合越复杂;Start、AI、DB、Store 等新项目还需要时间证明稳定性;headless 的自由会带来更多应用级责任;类型系统的优势也可能变成学习门槛。

所以,TanStack 的下一阶段不只是继续发布更多项目,而是要证明这些项目组合起来确实比各自为战更好用。如果它能在保持可移植性的同时,进一步降低全栈配置、SSR 调试、状态同步和 agent 使用的成本,那么它就可能从“优秀的开源工具集合”变成前端应用的通用基础层。

小黑把产品装上应用栈之船

最后

TanStack 的崛起,核心不在于它覆盖了多少产品,而在于它一直在尝试用同一套工程原则处理不同层次的问题:明确状态边界、强化类型合同、拆开核心与渲染、公开稳定性和取舍。

从 React Query 开始,它先解决了服务器状态的混乱,再把这套方法延伸到路由、全栈、数据视图与开发工具。未来它能走多远,取决于产品之间的组合是否足够稳定,也取决于这些抽象能否继续给真实项目带来比额外复杂度更多的收益。

感谢阅读这篇文章,我们下期再见。


not-by-ainot-by-ai
已读 0%
文章推荐
文章封面
从入门到放弃再到成功:一个前端程序员的后端 API 调试血泪史
更新于 2025-06-10
编程
文章封面
为什么我的 Tailwind css 类没生效
更新于 2025-02-13
编程
GitHubTwitter / XMediumDevJuejin

友情链接

Jimmy
Jimmy
老胡
老胡
Submara
Submara
Bruce Song
Bruce Song
Scarsu
Scarsu
宇阳
宇阳
Steven Lynn's Blog
Steven Lynn's Blog
OJ·Jimmy (Other Jimmy)
OJ·Jimmy (Other Jimmy)
liruifengv - Web 开发者,Astro 项目成员,开源爱好者。
liruifengv - Web 开发者,Astro 项目成员,开源爱好者。