Loading...
Loading...
Compare original and translation side by side
npm install @tanstack/react-start @tanstack/react-router
npm install -D @tanstack/router-pluginnpm install @tanstack/react-start @tanstack/react-router
npm install -D @tanstack/router-plugin// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
export default defineConfig({
plugins: [tanstackStart(), react()],
})// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
export default defineConfig({
plugins: [tanstackStart(), react()],
})// src/routes/__root.tsx
import {
HeadContent,
Outlet,
Scripts,
createRootRoute,
} from '@tanstack/react-router'
export const Route = createRootRoute({
head: () => ({
meta: [
{ charSet: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
],
}),
component: RootComponent,
})
function RootComponent() {
return (
<html lang="en">
<head>
<HeadContent />
</head>
<body>
<Outlet />
<Scripts />
</body>
</html>
)
}// src/routes/__root.tsx
import {
HeadContent,
Outlet,
Scripts,
createRootRoute,
} from '@tanstack/react-router'
export const Route = createRootRoute({
head: () => ({
meta: [
{ charSet: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
],
}),
component: RootComponent,
})
function RootComponent() {
return (
<html lang="en">
<head>
<HeadContent />
</head>
<body>
<Outlet />
<Scripts />
</body>
</html>
)
}npm install -D @tanstack/react-router-devtoolsimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
// In root layout, before </body>
<TanStackRouterDevtools position="bottom-right" />
<Scripts />TanStackDevtoolsnpm install -D @tanstack/react-devtoolsimport { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
import { TanStackDevtools } from '@tanstack/react-devtools'
// In root layout, before </body>
<TanStackDevtools
config={{ position: 'bottom-right' }}
plugins={[
{ name: 'TanStack Router', render: <TanStackRouterDevtoolsPanel /> },
// Add more plugins: Query, etc.
]}
/>
<Scripts />*PanelTanStackRouterDevtoolsPanelReactQueryDevtoolsPanelTanStackDevtoolsnpm install -D @tanstack/react-router-devtoolsimport { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
// 在根布局的</body>前添加
<TanStackRouterDevtools position="bottom-right" />
<Scripts />TanStackDevtoolsnpm install -D @tanstack/react-devtoolsimport { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'
import { TanStackDevtools } from '@tanstack/react-devtools'
// 在根布局的</body>前添加
<TanStackDevtools
config={{ position: 'bottom-right' }}
plugins={[
{ name: 'TanStack Router', render: <TanStackRouterDevtoolsPanel /> },
// 添加更多插件:Query等
]}
/>
<Scripts />TanStackDevtools*PanelTanStackRouterDevtoolsPanelReactQueryDevtoolsPanel| Priority | Rule | Description |
|---|---|---|
| CRITICAL | sf-server-functions | createServerFn, GET/POST methods, handler pattern |
| HIGH | sf-middleware | createMiddleware, client/server execution, chaining |
| HIGH | sf-validation | Input validation with Zod/Standard Schema, trust boundaries |
| HIGH | ssr-streaming | Streaming SSR with Suspense, TTFB optimization |
| HIGH | seo-head-management | head() per route, meta tags, HeadContent, OG tags |
| HIGH | auth-patterns | beforeLoad guards, session server fns, cookie forwarding |
| MEDIUM | ssr-hydration | Hydration mismatch prevention, client-only components |
| MEDIUM | ssr-prerendering | Static prerendering, ISR via Cache-Control |
| MEDIUM | api-routes | createAPIFileRoute, REST handlers, webhooks |
| MEDIUM | deployment | Hosting adapters, env vars, production config |
| MEDIUM | config-project-setup | Vite plugin, root route, project structure |
| 优先级 | 规则 | 描述 |
|---|---|---|
| 关键 | sf-server-functions | createServerFn、GET/POST方法、处理器模式 |
| 高 | sf-middleware | createMiddleware、客户端/服务器执行、链式调用 |
| 高 | sf-validation | 使用Zod/标准Schema进行输入验证、信任边界 |
| 高 | ssr-streaming | 结合Suspense的流式SSR、TTFB优化 |
| 高 | seo-head-management | 按路由配置head()、元标签、HeadContent、OG标签 |
| 高 | auth-patterns | beforeLoad守卫、会话服务器函数、Cookie转发 |
| 中 | ssr-hydration | 避免 hydration 不匹配、客户端专属组件 |
| 中 | ssr-prerendering | 静态预渲染、通过Cache-Control实现ISR |
| 中 | api-routes | createAPIFileRoute、REST处理器、Webhooks |
| 中 | deployment | 托管适配器、环境变量、生产环境配置 |
| 中 | config-project-setup | Vite插件、根路由、项目结构 |
createServerFncreateServerFnimport { createServerFn } from '@tanstack/react-start'
const getUser = createServerFn({ method: 'GET' })
.handler(async () => {
return db.users.findFirst()
})import { createServerFn } from '@tanstack/react-start'
const getUser = createServerFn({ method: 'GET' })
.handler(async () => {
return db.users.findFirst()
})const updateUser = createServerFn({ method: 'POST' })
.inputValidator((data: { name: string; email: string }) => data)
.handler(async ({ data }) => {
return db.users.update({ where: { email: data.email }, data })
})const updateUser = createServerFn({ method: 'POST' })
.inputValidator((data: { name: string; email: string }) => data)
.handler(async ({ data }) => {
return db.users.update({ where: { email: data.email }, data })
})windowdocumentwindowdocument// BAD — hydration mismatch
function Bad() {
return <span>{window.innerWidth}px</span>
}
// GOOD — use useEffect for client-only state
function Good() {
const [width, setWidth] = useState<number | null>(null)
useEffect(() => setWidth(window.innerWidth), [])
return <span>{width ?? '...'}px</span>
}// 错误写法 — 会导致hydration不匹配
function Bad() {
return <span>{window.innerWidth}px</span>
}
// 正确写法 — 使用useEffect处理客户端专属状态
function Good() {
const [width, setWidth] = useState<number | null>(null)
useEffect(() => setWidth(window.innerWidth), [])
return <span>{width ?? '...'}px</span>
}head()head()export const Route = createFileRoute('/about')({
head: () => ({
meta: [
{ title: 'About Us' },
{ name: 'description', content: 'Learn about our company' },
],
}),
})export const Route = createFileRoute('/about')({
head: () => ({
meta: [
{ title: 'About Us' },
{ name: 'description', content: 'Learn about our company' },
],
}),
})// routes/_authed.tsx
export const Route = createFileRoute('/_authed')({
beforeLoad: async ({ location }) => {
const user = await getCurrentUserFn()
if (!user) {
throw redirect({
to: '/login',
search: { redirect: location.href },
})
}
return { user }
},
})// routes/_authed.tsx
export const Route = createFileRoute('/_authed')({
beforeLoad: async ({ location }) => {
const user = await getCurrentUserFn()
if (!user) {
throw redirect({
to: '/login',
search: { redirect: location.href },
})
}
return { user }
},
})const createPost = createServerFn({ method: 'POST' })
.inputValidator((data: { title: string; body: string }) => data)
.handler(async ({ data }) => {
const post = await db.posts.create({ data })
return post
})const createPost = createServerFn({ method: 'POST' })
.inputValidator((data: { title: string; body: string }) => data)
.handler(async ({ data }) => {
const post = await db.posts.create({ data })
return post
})export const Route = createFileRoute('/dashboard')({
loader: async ({ context: { queryClient } }) => {
// Await critical data
await queryClient.ensureQueryData(userQueries.profile())
// Prefetch non-critical (streams in via Suspense)
queryClient.prefetchQuery(statsQueries.dashboard())
},
component: DashboardPage,
})
function DashboardPage() {
const { data: user } = useSuspenseQuery(userQueries.profile())
return (
<div>
<Header user={user} />
<Suspense fallback={<StatsSkeleton />}>
<DashboardStats />
</Suspense>
</div>
)
}export const Route = createFileRoute('/dashboard')({
loader: async ({ context: { queryClient } }) => {
// 等待关键数据加载完成
await queryClient.ensureQueryData(userQueries.profile())
// 预取非关键数据(通过Suspense流式加载)
queryClient.prefetchQuery(statsQueries.dashboard())
},
component: DashboardPage,
})
function DashboardPage() {
const { data: user } = useSuspenseQuery(userQueries.profile())
return (
<div>
<Header user={user} />
<Suspense fallback={<StatsSkeleton />}>
<DashboardStats />
</Suspense>
</div>
)
}