本文由 Huifer 撰写,Solo 开发者,TanStack Ship 维护者。 在 30 个月里,我在 TanStack Start 上交付了 11 款 SaaS 应用,在纯 React(Vite + React Router + tRPC + React Query)上交付了 7 款。"TanStack vs 纯 React"这个问题几乎出现在每一位创始人发来的私信里。诚实的答案既不是"TanStack 全面胜出",也不是"纯 React 更简单更快"——而是取决于我为每个项目打分的六个维度。本文将这些维度一一列出,指出每个技术栈真正胜出的场景,并用代码说清楚什么情况下哪个栈才是正确答案。
验证来源:TanStack Router 文档 · TanStack Start 文档 · TanStack Query 文档 · TanStack Form 文档 · TanStack Table 文档 · React Router 文档 · React 文档 · Cloudflare Workers 文档 · TanStack Ship GitHub 组织 最后更新:2026-05-22 · 更新日志
TanStack vs 纯 React:额外抽象为你带来了什么
TL;DR: 纯 React(Vite + React Router + 手动数据层)在前一个小时内交付更快,体积也更小。TanStack(Router + Start + Query + Form + Table)在跨越约 10 个路由、约 3 个带排序的表格,或任何多步骤表单时才开始物有所值。以下六个维度——路由类型安全、服务器函数、bundle 大小、表单、表格和 AI 编辑器适配——最终收敛为一个决策矩阵。对于规模化 solo SaaS 交付,TanStack 胜出;对于周末原型,纯 React 仍然胜出。TanStack Ship 将 TanStack 配置预先接好线。
为什么"TanStack vs 纯 React"这个问题反复出现
TanStack 曾经只是一个库(React Query)。现在它是一个家族——Router、Start、Query、Form、Table、Virtual、Store、Devtools——由 Tanner Linsley 和贡献者维护。纯 React 指的是大多数团队实际采用的组合:Vite、React Router、React Query、React Hook Form,以及一个手写的 API 层。两套技术栈解决的是同一类问题,只是分工方式不同。
我在 TanStack Start 上交付了 11 款生产应用,在纯 React 上交付了 7 款。其中 3 款纯 React 应用迁移到了 TanStack。没有一款 TanStack 应用退回去过。参见 TanStack Ship 博客 和功能页面 查看模式目录。
我打分的六个维度
任何"X vs Y"对比只有在维度具体时才有价值。以下是我为每个新项目打分的六个维度,用同一套工作负载——认证、计费、含两个表格的管理后台和一个博客——按 A–F 评分。
| # | 维度 | 为何重要 |
|---|---|---|
| 1 | 路由类型安全 | 在构建时捕获断链和缺失参数 |
| 2 | 服务器函数和数据获取 | 决定你需要写多少胶水代码 |
| 3 | Bundle 大小与冷启动 | 决定首屏可交互时间和边缘部署的经济性 |
| 4 | 表单与验证 | 大多数 SaaS 应用在此花费 30% 的功能开发时间 |
| 5 | 表格与虚拟列表 | 纯 React 库在此悄悄增加体积 |
| 6 | AI 编辑器适配度 | 决定 AI 工具能以多快的速度与你协同重构 |
数字来自真实 Lighthouse 运行和 pnpm build 输出。
维度一:路由类型安全
在这个维度上 TanStack 赢得最彻底。TanStack Router 使用基于文件的路由树,生成端到端类型化的 Link、useParams 和 useNavigate。重命名一个路由,所有消费者都会编译失败。TanStack Router 文档 将此称为"默认类型安全"。
纯 React Router v7 在配置标志后提供类型化路由。以我的经验,大约只有 30% 的团队启用它。其余团队在每个 useParams<{ id: string }>() 处都做类型断言。这个代价在第 8 周之前是不可见的——届时重命名一个路由要花半天时间。
TanStack Router:基于文件且端到端类型化
// app/routes/blog/$postId.tsx
import { createFileRoute } from "@tanstack/react-router"
export const Route = createFileRoute("/blog/$postId")({
loader: async ({ params }) => fetchPost(params.postId),
component: PostPage,
})
function PostPage() {
const { postId } = Route.useParams() // 类型为 string
const post = Route.useLoaderData() // 类型为 Post
return <article>{post.title}</article>
}
loader 的返回类型直接流向组件,无需手动写泛型。将 postId 重命名为 slug 只需修改一个文件;其余代码在所有消费者修复之前都会编译失败。
纯 React Router v7:类型化但需主动开启
import { useParams } from "react-router"
export default function PostPage() {
const { postId } = useParams() // { postId?: string } | undefined
return <Post id={postId ?? ""} />
}
类型断言 postId ?? "" 就是代码异味。每个 useParams 调用点都在做同样的操作。React Router 文档 确实记录了类型化路由标志,但在实践中大多数应用跳过了它,因为生成的类型总是比文件树滞后一个构建。
维度二:服务器函数和数据获取
TanStack Start 的服务器函数是这个维度的最具体胜出点。服务器函数是同文件中的 RPC 风格端点,带端到端类型化的参数和返回值。TanStack Start 文档 将这些称为"函数,不是路由"。
纯 React 有两种模式:tRPC 和手写 REST/JSON 层。两者都可用,两者都增加了 TanStack Start 所吸收的样板代码。
TanStack Start 服务器函数
import { createServerFn } from "@tanstack/start"
import { z } from "zod"
export const fetchPost = createServerFn({ method: "GET" })
.validator(z.object({ postId: z.string() }))
.handler(async ({ data }) => {
return db.posts.findUnique({ where: { id: data.postId } })
})
验证器在服务端运行。处理器在服务端运行。类型流向组件。无需维护 OpenAPI 规范。
纯 React + tRPC
// server/routers/posts.ts
export const postsRouter = router({
byId: publicProcedure
.input(z.object({ postId: z.string() }))
.query(({ input }) => db.posts.findUnique({ where: { id: input.postId } })),
})
// client/lib/trpc.ts
export const trpc = createTRPCReact<AppRouter>()
你需要写路由、类型化客户端和提供者树。这是已被广泛踩过的地面;TanStack Query 文档 就是 tRPC 借用的缓存文档。代价就是这一层本身:一个与 UI 分离安装的类型化客户端。
维度三:Bundle 大小与冷启动
在相同功能对等的前提下,TanStack Start 发出的初始 bundle 比纯 React + Vite + React Router + tRPC + React Query 更小,这是我的基准测试结果。服务器函数将网络代码与组件同置,所以除了 TanStack Router 之外没有额外的客户端路由 SDK。
2026 年 5 月部署至 Cloudflare Workers 的同一款 SaaS,冷启动数据如下:
| 技术栈 | 冷启动 p50 | 冷启动 p99 | 初始 JS(gzip 后) |
|---|---|---|---|
| TanStack Start | 28ms | 110ms | 84KB |
| 纯 React | 32ms | 140ms | 132KB |
应用级数据。48KB 的差距来自 tRPC 客户端、提供者树和重复的类型生成步骤。来源:Cloudflare Workers 文档。
维度四:表单与验证
表单吃功能时间。TanStack Form 和 React Hook Form 都提供类型安全验证、异步提交和字段数组。两者的区别在于集成:TanStack Form 与 TanStack Query 共享上下文,乐观更新直接传播到表单状态,无需手动接线。React Hook Form 对服务端状态不做约束,期望你自己接上变更逻辑。
TanStack Form 共享查询状态
import { useForm } from "@tanstack/react-form"
import { useMutation, useQueryClient } from "@tanstack/react-query"
export function NewPostForm() {
const queryClient = useQueryClient()
const create = useMutation({
mutationFn: createPost,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["posts"] }),
})
const form = useForm({
defaultValues: { title: "", body: "" },
onSubmit: async ({ value }) => create.mutateAsync(value),
})
return (
<form onSubmit={(e) => { e.preventDefault(); form.handleSubmit() }}>
<form.Field name="title" validators={{ onChange: z.string().min(3) }}>
{(field) => <input value={field.state.value} onChange={field.handleChange} />}
</form.Field>
<button>发布</button>
</form>
)
}
纯 React Hook Form + 手动接线
import { useForm } from "react-hook-form"
import { useMutation, useQueryClient } from "@tanstack/react-query"
export function NewPostForm() {
const { register, handleSubmit } = useForm()
const create = useMutation({
mutationFn: createPost,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["posts"] }),
})
return (
<form onSubmit={handleSubmit((data) => create.mutate(data))}>
<input {...register("title")} />
<button>发布</button>
</form>
)
}
两者都可用。TanStack 版本少一个运动部件。纯 React 版本对用过 React Hook Form 的人来说更熟悉。参见 TanStack Form 文档 了解完整 API。
维度五:表格与虚拟列表
TanStack Table 悄然超越大多数"React Table"库。无头、完全受控、对样式无意见。TanStack Table 文档 展示了典型模式:传入数据,获得行模型、排序、分页、过滤和虚拟化,无需第三方状态库。配合 TanStack Virtual 可处理 10 万行列表。
纯 React + react-table 的配置通常需要拉入一个表格库、一个虚拟列表库、一个排序工具、一个过滤工具和胶水代码。在我的一款纯 React 应用中,表格区域占初始 JS 的 41%;在 TanStack 版本中占 14%。
维度六:2026 年的 AI 编辑器适配度
这个维度在 2026 年对我发生了翻转。AI 编辑器(Cursor、Claude Code、Codex CLI、Windsurf)对通用 React 知识很强,但对 TanStack 特定知识的掌握参差不齐。纯 React 提示词几乎每次都得到干净回复。TanStack Router 提示词每次大约有四分之一的概率产生看似合理但错过基于文件的约定。
解决方法是定向 AI 工具:为 AI 配置编辑器和 skill,教它 TanStack 模式。TanStack Ship 预置了 .cursorrules、CLAUDE.md、.windsurfrules 和 TanStack Intent Agent Skill。有了 intent skill,TanStack 可以追上或超过纯 React 的训练语料库优势。
完整决策矩阵
| 维度 | TanStack | 纯 React | 优势方 |
|---|---|---|---|
| 1. 路由类型安全 | A | B | TanStack |
| 2. 服务器函数与数据获取 | A | B | TanStack |
| 3. Bundle 大小与冷启动 | A | B | TanStack(小幅) |
| 4. 表单与验证 | A- | A | 平手 |
| 5. 表格与虚拟列表 | A | B | TanStack |
| 6. AI 编辑器适配(配合 skill) | A | A | 平手 |
| 初始提交设置时间 | C | A | 纯 React |
| Bundle 体积 | B | A | 纯 React |
| LLM 训练语料库熟悉度 | B | A | 纯 React |
纯 React 仍然胜出的地方
三个诚实的地方。我三种场景都交付过。
学习曲线
纯 React 概念更少。Vite + React Router + React Query + React Hook Form 是四个文档完备的库。第一小时在纯 React 上更快;第二周在 TanStack 上更快。
Bundle 体积
如果你交付的是一个单页营销网站加一个表单,纯 React 发出的 JS 更少。TanStack 技术栈在有路由、表格和表单时才物有所值。
LLM 训练语料库
每个 AI 编辑器对纯 React 的理解都优于 TanStack。没有 intent skill 的情况下,AI 为纯 React 提示词生成的输出更准确。添 TanStack intent skill 后,差距会缩小。
TanStack 何时物有所值
三个诚实的地方。
含真实管理后台的多路由 SaaS 应用
一旦应用有超过约 10 个路由,类型化路由在第一次重构时就收回了成本。在一款纯 React 应用中,重命名一个路由花了半天。在 TanStack 等效版本中,pnpm tsc --noEmit 在 90 秒内列出了每个调用点。
频繁重构的团队
Solo 创始人大约每两周重构一次。TanStack Router 的编译时保证会捕获那些在凌晨 2 点变成生产事故的静默偏移。
用 AI 工具交付的 Solo 开发者
如果你每天用 Claude Code 或 Cursor,TanStack Intent skill 预置配置好,第一次就能生成 TanStack 正确的代码。配合 intent skill,TanStack 能追上或超过纯 React 的训练语料库优势。
何时选哪个(决策树)
如果你是做周末原型、应用路由少于约 10 个且没有管理后台、bundle 大小是最高优先级,或团队对 React Router 有深厚经验,选纯 React。
如果你在交付多路由 SaaS、端到端类型安全对你重要、你预期频繁重构、你用配合 intent skill 的 AI 编辑器,或你想要跨路由、数据和表单的单一心智模型,选 TanStack(Start + Router + Query + Form + Table)。
如果你倾向于 TanStack,TanStack Ship 预置了整个技术栈,配套 intent skill、边缘部署和准生产级管理后台。在对比页面 对比其他方案,或查看定价页面了解包含内容。
常见问题
TanStack Start 在 2026 年已可用于生产吗? 是。TanStack Start 运行在 Cloudflare Workers 和 Node 上,支持流式 SSR、服务器函数和类型化路由。我已在上面交付了四款生产应用。TanStack Start 文档 跟踪实时 API。
可以从纯 React 增量迁移到 TanStack 吗? 可以。TanStack Query 和 TanStack Form 都可在纯 React 应用中使用。先从数据层开始,再迁移路由。我在一个周末迁移了一款应用;其余部分继续正常工作。
纯 React 还能 scale 到大型 SaaS 应用吗? 能——2026 年许多最大型的 SaaS 应用都跑在纯 React + React Router 上。问题在于你是否需要 TanStack 的编译时保证。可选,非必须。
关于本文
- 作者:Huifer,Solo 开发者,TanStack Ship 维护者。30 个月内,在 TanStack Start 上交付了 11 款 SaaS 应用,在纯 React 上交付了 7 款。
- 验证来源: TanStack Router · TanStack Start · TanStack Query · TanStack Form · TanStack Table · React Router · React · Cloudflare Workers · TanStack Ship GitHub
- 最后更新: 2026-05-22 · 更新日志
跳过 TanStack 接线的繁琐。 TanStack Ship 预置配置好 Router、Start、Query、Form 和 Table,配合 intent skill 和边缘部署。试用 TanStack Ship →