In TanStack Start werden Routen, die der anfänglichen Anfrage entsprechen, standardmäßig auf dem Server gerendert. Das bedeutet, dass beforeLoad und loader auf dem Server ausgeführt werden, gefolgt vom Rendern der Routenkomponenten. Der resultierende HTML wird an den Client gesendet, der das Markup in eine vollständig interaktive Anwendung "hydriert".
Es gibt jedoch Fälle, in denen Sie SSR für bestimmte oder alle Routen deaktivieren möchten, z. B.
Die Funktion "Selektives SSR" von TanStack Start ermöglicht Ihnen die Konfiguration
Der SPA-Modus von TanStack Start deaktiviert die serverseitige Ausführung von beforeLoad und loader sowie das serverseitige Rendering von Routenkomponenten vollständig. Selektives SSR ermöglicht Ihnen die Konfiguration der serverseitigen Verarbeitung auf Basis einzelner Routen, entweder statisch oder dynamisch.
Sie können steuern, wie eine Route während der anfänglichen Serveranfrage behandelt wird, indem Sie die Eigenschaft ssr verwenden. Wenn diese Eigenschaft nicht gesetzt ist, ist der Standardwert true. Sie können diesen Standardwert mit der Option defaultSsr in createRouter ändern.
// src/router.tsx
import { createRouter as createTanStackRouter } from '@tanstack/solid-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
defaultPendingComponent: () => <div>Loading...</div>,
// Disable SSR by default
defaultSsr: false,
})
return router
}
// src/router.tsx
import { createRouter as createTanStackRouter } from '@tanstack/solid-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
defaultPendingComponent: () => <div>Loading...</div>,
// Disable SSR by default
defaultSsr: false,
})
return router
}
Dies ist das Standardverhalten, sofern nicht anders konfiguriert. Bei der anfänglichen Anfrage wird Folgendes ausgeführt:
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: true,
beforeLoad: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
loader: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
component: () => <div>This component is rendered on the client</div>,
})
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: true,
beforeLoad: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
loader: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
component: () => <div>This component is rendered on the client</div>,
})
Dies deaktiviert serverseitig
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: false,
beforeLoad: () => {
console.log('Executes on the client during hydration')
},
loader: () => {
console.log('Executes on the client during hydration')
},
component: () => <div>This component is rendered on the client</div>,
})
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: false,
beforeLoad: () => {
console.log('Executes on the client during hydration')
},
loader: () => {
console.log('Executes on the client during hydration')
},
component: () => <div>This component is rendered on the client</div>,
})
Diese Hybridoption wird
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: 'data-only',
beforeLoad: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
loader: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
component: () => <div>This component is rendered on the client</div>,
})
// src/routes/posts/$postId.tsx
export const Route = createFileRoute('/posts/$postId')({
ssr: 'data-only',
beforeLoad: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
loader: () => {
console.log('Executes on the server during the initial request')
console.log('Executes on the client for subsequent navigation')
},
component: () => <div>This component is rendered on the client</div>,
})
Für mehr Flexibilität können Sie die funktionale Form der Eigenschaft ssr verwenden, um zur Laufzeit zu entscheiden, ob eine Route SSR-gerendert werden soll.
// src/routes/docs/$docType/$docId.tsx
export const Route = createFileRoute('/docs/$docType/$docId')({
validateSearch: z.object({ details: z.boolean().optional() }),
ssr: ({ params, search }) => {
if (params.status === 'success' && params.value.docType === 'sheet') {
return false
}
if (search.status === 'success' && search.value.details) {
return 'data-only'
}
},
beforeLoad: () => {
console.log('Executes on the server depending on the result of ssr()')
},
loader: () => {
console.log('Executes on the server depending on the result of ssr()')
},
component: () => <div>This component is rendered on the client</div>,
})
// src/routes/docs/$docType/$docId.tsx
export const Route = createFileRoute('/docs/$docType/$docId')({
validateSearch: z.object({ details: z.boolean().optional() }),
ssr: ({ params, search }) => {
if (params.status === 'success' && params.value.docType === 'sheet') {
return false
}
if (search.status === 'success' && search.value.details) {
return 'data-only'
}
},
beforeLoad: () => {
console.log('Executes on the server depending on the result of ssr()')
},
loader: () => {
console.log('Executes on the server depending on the result of ssr()')
},
component: () => <div>This component is rendered on the client</div>,
})
Die Funktion ssr wird nur auf dem Server während der anfänglichen Anfrage ausgeführt und aus dem Client-Bundle entfernt.
search und params werden nach der Validierung als diskriminierte Vereinigung übergeben.
params:
| { status: 'success'; value: Expand<ResolveAllParamsFromParent<TParentRoute, TParams>> }
| { status: 'error'; error: unknown }
search:
| { status: 'success'; value: Expand<ResolveFullSearchSchema<TParentRoute, TSearchValidator>> }
| { status: 'error'; error: unknown }
params:
| { status: 'success'; value: Expand<ResolveAllParamsFromParent<TParentRoute, TParams>> }
| { status: 'error'; error: unknown }
search:
| { status: 'success'; value: Expand<ResolveFullSearchSchema<TParentRoute, TSearchValidator>> }
| { status: 'error'; error: unknown }
Wenn die Validierung fehlschlägt, ist status error und error enthält die Details des Fehlers. Andernfalls ist status success und value enthält die validierten Daten.
Zur Laufzeit erbt eine Kind-Route die Konfiguration des selektiven SSR ihrer Eltern. Zum Beispiel:
root { ssr: undefined }
posts { ssr: false }
$postId { ssr: true }
root { ssr: undefined }
posts { ssr: false }
$postId { ssr: true }
Ein weiteres Beispiel
root { ssr: undefined }
posts { ssr: 'data-only' }
$postId { ssr: true }
details { ssr: false }
root { ssr: undefined }
posts { ssr: 'data-only' }
$postId { ssr: true }
details { ssr: false }
Für die erste Route mit ssr: false oder ssr: 'data-only' rendert der Server die pendingComponent der Route als Fallback. Wenn pendingComponent nicht konfiguriert ist, wird die defaultPendingComponent gerendert. Wenn keines von beiden konfiguriert ist, wird kein Fallback gerendert.
Auf dem Client während der Hydration wird dieser Fallback für mindestens minPendingMs (oder defaultPendingMinMs, wenn nicht konfiguriert) angezeigt, auch wenn die Route keine beforeLoad oder loader definiert hat.
Sie können das serverseitige Rendering der Root-Routenkomponente deaktivieren. Die <html>-Shell muss jedoch weiterhin auf dem Server gerendert werden. Diese Shell wird über die Eigenschaft shellComponent konfiguriert und nimmt eine einzelne Eigenschaft children entgegen. Die shellComponent wird immer SSR-gerendert und umschließt die Root- component, die Root- errorComponent oder die Root- notFound-Komponente entsprechend.
Ein minimales Setup einer Root-Route mit deaktiviertem SSR für die Routenkomponente sieht wie folgt aus:
import type * as Solid from 'solid-js'
import {
HeadContent,
Outlet,
Scripts,
createRootRoute,
} from '@tanstack/solid-router'
export const Route = createRootRoute({
shellComponent: RootShell,
component: RootComponent,
errorComponent: () => <div>Error</div>,
notFoundComponent: () => <div>Not found</>,
ssr: false // or `defaultSsr: false` on the router
})
function RootShell({ children }: { children: Solid.JSX.Element }) {
return (
<>
<HeadContent />
{children}
<Scripts />
</>
)
}
function RootComponent() {
return (
<div>
<h1>This component will be rendered on the client</h1>
<Outlet />
</div>
)
}
import type * as Solid from 'solid-js'
import {
HeadContent,
Outlet,
Scripts,
createRootRoute,
} from '@tanstack/solid-router'
export const Route = createRootRoute({
shellComponent: RootShell,
component: RootComponent,
errorComponent: () => <div>Error</div>,
notFoundComponent: () => <div>Not found</>,
ssr: false // or `defaultSsr: false` on the router
})
function RootShell({ children }: { children: Solid.JSX.Element }) {
return (
<>
<HeadContent />
{children}
<Scripts />
</>
)
}
function RootComponent() {
return (
<div>
<h1>This component will be rendered on the client</h1>
<Outlet />
</div>
)
}
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.