为何是 TanStack Start
我们选择 TanStack Start 而非 Next.js 的原因
Next.js 是成熟的框架,生态完善。我们选择 TanStack Start,主要是因为它在渲染策略和类型安全上更符合我们的需求。
这不是一篇批评 Next.js 的文章。Next.js 的向后兼容性做得很好。
TanStack Start 2026:不只是 SPA 框架
很多人对 TanStack Start 的印象还停留在"客户端优先的 SPA 框架"。2026 年的 TanStack Start 已经支持完整的服务端渲染能力:
- Full-document SSR:完整的服务端渲染
- Streaming:流式渲染
- Static Prerendering:静态预渲染
- ISR(增量静态再生成):动态内容的静态化方案
- Selective SSR:按需选择哪些页面需要 SSR
- SPA Mode:纯客户端渲染模式
- Server Functions:类型安全的服务端函数调用
- LLM Optimization (LLMO):针对 AI 爬虫的优化
Next.js 能做的渲染方式,TanStack Start 基本都支持了。那我们为什么还是选了它?
Next.js 的学习曲线
先说说 Next.js 的学习成本。新手往往只是想把产品做出来,但 App Router 引入的概念确实不少。
概念复杂度
Next.js App Router 引入了这些概念:
- Server Components vs Client Components:默认是服务端组件,需要
"use client"切换 - 序列化边界:不能在 Server Component 中传递函数、类实例给 Client Component
- 水合错误(Hydration Mismatch):服务端和客户端渲染不一致时很难调试
"use server"指令:一个字符串变成服务器组件或者服务端函数
新手光是理解"这行代码在哪里执行"就够头疼的。
缓存机制
Next.js 的缓存系统比较复杂:
// 这个 fetch 会被缓存吗?
const data = await fetch('/api/data');
// 加上这个呢?
const data = await fetch('/api/data', { cache: 'no-store' });
// 还是需要这个?
export const dynamic = 'force-dynamic';
// 或者用 revalidate?
export const revalidate = 60;Next.js 有多层缓存:Data Cache、Full Route Cache、Router Cache... 它们的交互方式连资深开发者也经常搞混。v14 默认缓存,v15 又改成默认不缓存,这种变化增加了理解成本。
两套路由系统
Next.js 同时存在 Pages Router 和 App Router:
- 网上教程混杂,新手不知道该学哪个
- 两套系统的概念和 API 差异大
- 从 Pages Router 迁移到 App Router 需要改动不少代码
调试
Next.js 的隐式行为让调试变得麻烦:
- 页面没更新?可能是缓存问题,但不知道是哪层
- 组件报错?可能是服务端/客户端边界问题,错误信息不够清晰
- 性能问题?不确定是 SSR、水合还是客户端渲染的问题
TanStack Start 的特点
TanStack Start 的学习曲线相对平缓:
- 没有 Server Components:所有组件都是你熟悉的 React 组件
- 显式的渲染策略:每个路由的 SSR/SPA/静态行为在代码中明确定义
- 熟悉的数据获取:TanStack Query 的
useQuery模式 - 类型系统帮你检查:错误在编译时暴露
TanStack Start 也有学习成本,但它的心智模型更接近传统 React 开发。
我们选择它的原因
1. 渲染策略由你决定
Next.js 的设计是"服务端优先"——默认所有组件都是 Server Components,需要显式声明 "use client" 切换到客户端。
TanStack Start 不同:你来决定每个页面的渲染策略。
// 纯 SPA 模式:适合仪表盘、AI 聊天等高交互应用
export const Route = createFileRoute('/dashboard')({
ssr: false,
component: Dashboard,
});
// SSR 模式:适合需要 SEO 的页面
export const Route = createFileRoute('/blog/$slug')({
loader: async ({ params }) => fetchPost(params.slug),
component: BlogPost,
});
// 静态预渲染:适合内容不常变化的页面
export const Route = createFileRoute('/about')({
preload: 'intent',
component: About,
});你可以在同一个应用中混用不同的渲染策略,不被框架的默认行为限制。
2. 类型安全的路由系统
TanStack Router 的类型安全做得很好:
// 文件名:$threadId.tsx
export const Route = createFileRoute('/chat/$threadId')({
loader: async ({ params }) => {
// params.threadId 的类型自动推断为 string
return fetchThread(params.threadId);
},
component: () => {
const { threadId } = Route.useParams();
// 编译时就知道 threadId 一定存在
}
});重命名文件时,开发服务器会自动更新相关代码。链接拼错或参数类型不匹配会在编译时报错。
3. Server Functions vs Server Actions
两者都允许从客户端调用服务器代码,设计上有区别:
// TanStack Server Function:类型安全的 RPC
export const updateUser = createServerFn()
.validator((data: { name: string; age: number }) => data)
.handler(async ({ data }) => {
// data 的类型自动推断
return db.users.update(data);
});
// 客户端调用(完全类型安全)
await updateUser({ name: 'Alice', age: 30 });Server Functions 更像类型安全的 RPC,内置验证器和中间件支持,调用方式和普通函数一样。
4. Vite 构建
TanStack Start 基于 Vite 构建,开发体验比 Webpack 时代好不少:
| 维度 | Next.js + Turbopack | TanStack Start + Vite |
|---|---|---|
| 启动速度 | 大型项目 5-10 秒 | 毫秒级 |
| HMR 响应 | 偶尔卡顿 | 基本即时 |
| 插件生态 | 需适配 | 直接使用 Vite 插件 |
技术基础
TanStack Start 是新框架,但构建在成熟的技术上:
- TanStack Router:类型安全路由
- TanStack Query:React 数据获取库
- Vite:快速的开发构建
- Nitro:来自 Nuxt 团队的服务端基础设施
挑战
早期框架的代价
TanStack Start 是较新的框架,API 已稳定但可能存在边缘情况。
相比 Next.js 多年积累的文档和社区解答,TanStack Start 的资料还在完善中。遇到问题时,你可能需要直接看源码或在 Discord 里提问。
Next.js 的优点
Next.js 仍然是好框架:
- 向后兼容性好:Next 10 的应用可以升级到 Next 16,改动不大
- 稳定:多年持续维护
- 生态成熟:部署、监控、边缘计算都有成熟方案
建议
继续用 Next.js:
- 团队熟悉 Next.js,项目运行正常
- 需要 React Server Components
- 不想踩新框架的坑
考虑 TanStack Start:
- 构建交互密集型应用(仪表盘、AI 聊天、协作工具)
- 看重端到端的类型安全
- 想要更灵活的渲染策略
- 能接受早期框架的文档和生态不完善
总结
我们选 TanStack Start 不是因为 Next.js 不好,而是它更符合我们的需求:
- 灵活的渲染策略,同一应用可以混用 SSR、SPA、静态预渲染
- 端到端的类型安全
- 行为显式,没有隐藏的魔法
选什么框架取决于你的需求。了解清楚需求,选合适的工具就行。