Diese Anleitung hilft Ihnen, die Grundlagen der Funktionsweise von TanStack Start zu verstehen, unabhängig davon, wie Sie Ihr Projekt eingerichtet haben.
TanStack Start wird von Vite und TanStack Router angetrieben.
Dies ist die Datei, die das Verhalten des in Start verwendeten TanStack Routers bestimmt. Hier können Sie alles konfigurieren, von der standardmäßigen Preloading-Funktionalität bis hin zur Aktualität des Caching.
// src/router.tsx
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
})
return router
}
declare module '@tanstack/react-router' {
interface Register {
router: ReturnType<typeof createRouter>
}
}
// src/router.tsx
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
})
return router
}
declare module '@tanstack/react-router' {
interface Register {
router: ReturnType<typeof createRouter>
}
}
Die Datei routeTree.gen.ts wird generiert, wenn Sie TanStack Start (über npm run dev oder npm run start) zum ersten Mal ausführen. Diese Datei enthält den generierten Routenbaum und eine Handvoll TS-Dienstprogramme, die TanStack Start vollständig typsicher machen.
Hinweis
Der Server-Einstiegspunkt ist standardmäßig optional. Wenn keiner bereitgestellt wird, kümmert sich TanStack Start automatisch um den Server-Einstiegspunkt für Sie, indem es die folgende Standardkonfiguration verwendet.
Dies geschieht über die Datei src/server.ts
// src/server.ts
import {
createStartHandler,
defaultStreamHandler,
} from '@tanstack/react-start/server'
import { createRouter } from './router'
export default createStartHandler({
createRouter,
})(defaultStreamHandler)
// src/server.ts
import {
createStartHandler,
defaultStreamHandler,
} from '@tanstack/react-start/server'
import { createRouter } from './router'
export default createStartHandler({
createRouter,
})(defaultStreamHandler)
Unabhängig davon, ob wir unsere App statisch generieren oder dynamisch bereitstellen, ist die Datei server.ts der Einstiegspunkt für alle SSR-bezogenen Arbeiten.
Hinweis
Der Client-Einstiegspunkt ist standardmäßig optional. Wenn keiner bereitgestellt wird, kümmert sich TanStack Start automatisch um den Client-Einstiegspunkt für Sie, indem es die folgende Standardkonfiguration verwendet.
Das Übertragen unseres HTMLs an den Client ist nur die halbe Miete. Sobald es dort ist, müssen wir unser clientseitiges JavaScript hydrieren, sobald die Route auf dem Client aufgelöst ist. Dies geschieht durch die Hydrierung der Wurzel unserer Anwendung mit der Komponente StartClient
// src/client.tsx
import { StartClient } from '@tanstack/react-start'
import { StrictMode } from 'react'
import { hydrateRoot } from 'react-dom/client'
import { createRouter } from './router'
const router = createRouter()
hydrateRoot(
document,
<StrictMode>
<StartClient router={router} />
</StrictMode>,
)
// src/client.tsx
import { StartClient } from '@tanstack/react-start'
import { StrictMode } from 'react'
import { hydrateRoot } from 'react-dom/client'
import { createRouter } from './router'
const router = createRouter()
hydrateRoot(
document,
<StrictMode>
<StartClient router={router} />
</StrictMode>,
)
Dies ermöglicht es uns, das clientseitige Routing zu starten, sobald die anfängliche Serveranfrage des Benutzers erfüllt wurde.
Abgesehen vom Client-Einstiegspunkt (der standardmäßig optional ist), ist die Route __root Ihrer Anwendung der Einstiegspunkt. Der Code in dieser Datei umschließt alle anderen Routen in der App, einschließlich Ihrer Startseite. Sie verhält sich wie eine pfadlose Layout-Route für Ihre gesamte Anwendung.
Da sie immer gerendert wird, ist sie der perfekte Ort, um Ihre Anwendungs-Shell zu konstruieren und sich um beliebige globale Logik zu kümmern.
// src/routes/__root.tsx
import {
Outlet,
createRootRoute,
HeadContent,
Scripts,
} from '@tanstack/react-router'
import type { ReactNode } from 'react'
export const Route = createRootRoute({
head: () => ({
meta: [
{
charSet: 'utf-8',
},
{
name: 'viewport',
content: 'width=device-width, initial-scale=1',
},
{
title: 'TanStack Start Starter',
},
],
}),
component: RootComponent,
})
function RootComponent() {
return (
<RootDocument>
<Outlet />
</RootDocument>
)
}
function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
{children}
<Scripts />
</body>
</html>
)
}
// src/routes/__root.tsx
import {
Outlet,
createRootRoute,
HeadContent,
Scripts,
} from '@tanstack/react-router'
import type { ReactNode } from 'react'
export const Route = createRootRoute({
head: () => ({
meta: [
{
charSet: 'utf-8',
},
{
name: 'viewport',
content: 'width=device-width, initial-scale=1',
},
{
title: 'TanStack Start Starter',
},
],
}),
component: RootComponent,
})
function RootComponent() {
return (
<RootDocument>
<Outlet />
</RootDocument>
)
}
function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
{children}
<Scripts />
</body>
</html>
)
}
Routen sind ein umfassendes Feature von TanStack Router und werden im Routing-Leitfaden ausführlich behandelt. Zusammenfassend lässt sich sagen:
// src/routes/index.tsx
import * as fs from 'node:fs'
import { createFileRoute, useRouter } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
const filePath = 'count.txt'
async function readCount() {
return parseInt(
await fs.promises.readFile(filePath, 'utf-8').catch(() => '0'),
)
}
const getCount = createServerFn({
method: 'GET',
}).handler(() => {
return readCount()
})
const updateCount = createServerFn({ method: 'POST' })
.validator((d: number) => d)
.handler(async ({ data }) => {
const count = await readCount()
await fs.promises.writeFile(filePath, `${count + data}`)
})
export const Route = createFileRoute('/')({
component: Home,
loader: async () => await getCount(),
})
function Home() {
const router = useRouter()
const state = Route.useLoaderData()
return (
<button
type="button"
onClick={() => {
updateCount({ data: 1 }).then(() => {
router.invalidate()
})
}}
>
Add 1 to {state}?
</button>
)
}
// src/routes/index.tsx
import * as fs from 'node:fs'
import { createFileRoute, useRouter } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
const filePath = 'count.txt'
async function readCount() {
return parseInt(
await fs.promises.readFile(filePath, 'utf-8').catch(() => '0'),
)
}
const getCount = createServerFn({
method: 'GET',
}).handler(() => {
return readCount()
})
const updateCount = createServerFn({ method: 'POST' })
.validator((d: number) => d)
.handler(async ({ data }) => {
const count = await readCount()
await fs.promises.writeFile(filePath, `${count + data}`)
})
export const Route = createFileRoute('/')({
component: Home,
loader: async () => await getCount(),
})
function Home() {
const router = useRouter()
const state = Route.useLoaderData()
return (
<button
type="button"
onClick={() => {
updateCount({ data: 1 }).then(() => {
router.invalidate()
})
}}
>
Add 1 to {state}?
</button>
)
}
TanStack Start baut zu 100% auf TanStack Router auf, sodass Ihnen alle Navigationsfunktionen von TanStack Router zur Verfügung stehen. Zusammenfassend:
Hier ist ein kurzes Beispiel, wie Sie die Komponente Link verwenden können, um zu einer neuen Route zu navigieren
import { Link } from '@tanstack/react-router'
function Home() {
return <Link to="/about">About</Link>
}
import { Link } from '@tanstack/react-router'
function Home() {
return <Link to="/about">About</Link>
}
Weitere detaillierte Informationen zur Navigation finden Sie im Navigationsleitfaden.
Möglicherweise haben Sie die oben mit createServerFn erstellte Serverfunktion bemerkt. Dies ist eines der mächtigsten Features von TanStack, das es Ihnen ermöglicht, serverseitige Funktionen zu erstellen, die sowohl auf dem Server während SSR als auch vom Client aufgerufen werden können!
Hier ist ein kurzer Überblick darüber, wie Serverfunktionen funktionieren
Hier ist ein kurzes Beispiel, wie Sie Serverfunktionen verwenden können, um Daten vom Server abzurufen und zurückzugeben
import { createServerFn } from '@tanstack/react-start'
import * as fs from 'node:fs'
import { z } from 'zod'
const getUserById = createServerFn({ method: 'GET' })
// Always validate data sent to the function, here we use Zod
.validator(z.string())
// The handler function is where you perform the server-side logic
.handler(async ({ data }) => {
return db.query.users.findFirst({ where: eq(users.id, data) })
})
// Somewhere else in your application
const user = await getUserById({ data: '1' })
import { createServerFn } from '@tanstack/react-start'
import * as fs from 'node:fs'
import { z } from 'zod'
const getUserById = createServerFn({ method: 'GET' })
// Always validate data sent to the function, here we use Zod
.validator(z.string())
// The handler function is where you perform the server-side logic
.handler(async ({ data }) => {
return db.query.users.findFirst({ where: eq(users.id, data) })
})
// Somewhere else in your application
const user = await getUserById({ data: '1' })
Um mehr über Serverfunktionen zu erfahren, lesen Sie den Serverfunktionen-Leitfaden.
Serverfunktionen können auch verwendet werden, um Mutationen auf dem Server durchzuführen. Dies geschieht ebenfalls mit derselben Funktion createServerFn, erfordert jedoch zusätzlich die Invalidierung aller Daten auf dem Client, die von der Mutation betroffen waren.
Hier ist ein kurzes Beispiel, wie Sie Serverfunktionen verwenden können, um eine Mutation auf dem Server durchzuführen und die Daten auf dem Client zu invalidieren
import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
import { dbUpdateUser } from '...'
const UserSchema = z.object({
id: z.string(),
name: z.string(),
})
export type User = z.infer<typeof UserSchema>
export const updateUser = createServerFn({ method: 'POST' })
.validator(UserSchema)
.handler(({ data }) => dbUpdateUser(data))
// Somewhere else in your application
import { useQueryClient } from '@tanstack/react-query'
import { useRouter } from '@tanstack/react-router'
import { useServerFn } from '@tanstack/react-start'
import { updateUser, type User } from '...'
export function useUpdateUser() {
const router = useRouter()
const queryClient = useQueryClient()
const _updateUser = useServerFn(updateUser)
return useCallback(
async (user: User) => {
const result = await _updateUser({ data: user })
router.invalidate()
queryClient.invalidateQueries({
queryKey: ['users', 'updateUser', user.id],
})
return result
},
[router, queryClient, _updateUser],
)
}
// Somewhere else in your application
import { useUpdateUser } from '...'
function MyComponent() {
const updateUser = useUpdateUser()
const onClick = useCallback(async () => {
await updateUser({ id: '1', name: 'John' })
console.log('Updated user')
}, [updateUser])
return <button onClick={onClick}>Click Me</button>
}
import { createServerFn } from '@tanstack/react-start'
import { z } from 'zod'
import { dbUpdateUser } from '...'
const UserSchema = z.object({
id: z.string(),
name: z.string(),
})
export type User = z.infer<typeof UserSchema>
export const updateUser = createServerFn({ method: 'POST' })
.validator(UserSchema)
.handler(({ data }) => dbUpdateUser(data))
// Somewhere else in your application
import { useQueryClient } from '@tanstack/react-query'
import { useRouter } from '@tanstack/react-router'
import { useServerFn } from '@tanstack/react-start'
import { updateUser, type User } from '...'
export function useUpdateUser() {
const router = useRouter()
const queryClient = useQueryClient()
const _updateUser = useServerFn(updateUser)
return useCallback(
async (user: User) => {
const result = await _updateUser({ data: user })
router.invalidate()
queryClient.invalidateQueries({
queryKey: ['users', 'updateUser', user.id],
})
return result
},
[router, queryClient, _updateUser],
)
}
// Somewhere else in your application
import { useUpdateUser } from '...'
function MyComponent() {
const updateUser = useUpdateUser()
const onClick = useCallback(async () => {
await updateUser({ id: '1', name: 'John' })
console.log('Updated user')
}, [updateUser])
return <button onClick={onClick}>Click Me</button>
}
Weitere Informationen zu Mutationen finden Sie im Mutationsleitfaden.
Ein weiteres mächtiges Feature von TanStack Router ist das Datenladen. Dies ermöglicht es Ihnen, Daten für SSR abzurufen und Routendaten vor dem Rendern vorzuladen. Dies geschieht mit der Funktion loader einer Route.
Hier ist ein kurzer Überblick darüber, wie das Datenladen funktioniert
Weitere Informationen zum Datenladen finden Sie im Datenladen-Leitfaden.
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.