Für Anwendungen, die kein SSR für SEO, Crawler oder Performance-Gründe benötigen, kann es wünschenswert sein, statisches HTML an Ihre Benutzer auszuliefern, das die "Shell" Ihrer Anwendung enthält (oder sogar vorgerendertes HTML für bestimmte Routen), welches die notwendigen html-, head- und body-Tags enthält, um Ihre Anwendung nur auf dem Client zu starten.
Kein SSR bedeutet nicht, auf serverseitige Funktionen zu verzichten! SPA-Modi lassen sich sehr gut mit serverseitigen Funktionen wie Serverfunktionen und/oder Serverrouten oder sogar anderen externen APIs kombinieren. Es bedeutet **lediglich, dass das anfängliche Dokument nicht das vollständig gerenderte HTML Ihrer Anwendung enthält, bis es auf dem Client mit JavaScript gerendert wurde**.
Nach der Aktivierung des SPA-Modus wird ein Start-Build einen zusätzlichen Vorab-Rendering-Schritt durchführen, um die Shell zu generieren. Dies geschieht durch
Hinweis
Andere Routen können ebenfalls vorab gerendert werden und es wird empfohlen, im SPA-Modus so viel wie möglich vorab zu rendern, dies ist jedoch für die Funktionsweise des SPA-Modus nicht erforderlich.
Um den SPA-Modus zu konfigurieren, gibt es einige Optionen, die Sie den Optionen Ihres Start-Plugins hinzufügen können.
// vite.config.ts
export default defineConfig({
plugins: [
TanStackStart({
spa: {
enabled: true,
},
}),
],
})
// vite.config.ts
export default defineConfig({
plugins: [
TanStackStart({
spa: {
enabled: true,
},
}),
],
})
Das Bereitstellen einer rein clientseitigen SPA auf einem Host oder CDN erfordert oft die Verwendung von Weiterleitungen, um sicherzustellen, dass URLs ordnungsgemäß zur SPA-Shell umgeschrieben werden. Das Ziel jeder Bereitstellung sollte diese Prioritäten in dieser Reihenfolge beinhalten:
Wir verwenden die _redirects-Datei von Netlify, um alle 404-Anfragen zur SPA-Shell umzuschreiben.
# Catch all other 404 requests and rewrite them to the SPA shell
/* /_shell.html 200
# Catch all other 404 requests and rewrite them to the SPA shell
/* /_shell.html 200
Auch hier können wir mit der _redirects-Datei von Netlify bestimmte Unterpfade erlauben, die an den Server weitergeleitet werden.
# Allow requests to /_serverFn/* to be routed through to the server (If you have configured your server function base path to be something other than /_serverFn, use that instead)
/_serverFn/* /_serverFn/:splat 200
# Allow any requests to /api/* to be routed through to the server (Server routes can be created at any path, so you must ensure that any server routes you want to use are under this path, or simply add additional redirects for each server route base you want to expose)
/api/* /api/:splat 200
# Catch all other 404 requests and rewrite them to the SPA shell
/* /_shell.html 200
# Allow requests to /_serverFn/* to be routed through to the server (If you have configured your server function base path to be something other than /_serverFn, use that instead)
/_serverFn/* /_serverFn/:splat 200
# Allow any requests to /api/* to be routed through to the server (Server routes can be created at any path, so you must ensure that any server routes you want to use are under this path, or simply add additional redirects for each server route base you want to expose)
/api/* /api/:splat 200
# Catch all other 404 requests and rewrite them to the SPA shell
/* /_shell.html 200
Der Standard-Pfadname, der zum Generieren der SPA-Shell verwendet wird, ist /. Dies nennen wir den Shell-Maskenpfad. Da übereinstimmende Routen nicht enthalten sind, ist der zur Generierung der Shell verwendete Pfadname weitgehend irrelevant, aber er ist konfigurierbar.
Hinweis
Es wird empfohlen, den Standardwert von / als Shell-Maskenpfad beizubehalten.
// vite.config.ts
export default defineConfig({
plugins: [
tanstackStart({
spa: {
maskPath: '/app',
},
}),
],
})
// vite.config.ts
export default defineConfig({
plugins: [
tanstackStart({
spa: {
maskPath: '/app',
},
}),
],
})
Die Option "prerender" wird verwendet, um das Vorab-Rendering-Verhalten der SPA-Shell zu konfigurieren und akzeptiert dieselben "prerender"-Optionen wie in unserem Leitfaden zum Vorab-Rendering.
Standardmäßig sind die folgenden prerender-Optionen eingestellt:
Das bedeutet, dass die Shell standardmäßig nicht nach Links durchsucht wird, um weitere Vorab-Renderings zu verfolgen, und fehlgeschlagene Vorab-Renderings nicht wiederholt werden.
Sie können diese Optionen jederzeit überschreiben, indem Sie Ihre eigenen "prerender"-Optionen bereitstellen.
// vite.config.ts
export default defineConfig({
plugins: [
TanStackStart({
spa: {
prerender: {
outputPath: '/custom-shell',
crawlLinks: true,
retryCount: 3,
},
},
}),
],
})
// vite.config.ts
export default defineConfig({
plugins: [
TanStackStart({
spa: {
prerender: {
outputPath: '/custom-shell',
crawlLinks: true,
retryCount: 3,
},
},
}),
],
})
Die Anpassung der HTML-Ausgabe der SPA-Shell kann nützlich sein, wenn Sie
Um diesen Prozess zu vereinfachen, ist eine isShell()-Funktion auf der router-Instanz verfügbar.
// src/routes/root.tsx
export default function Root() {
const isShell = useRouter().isShell()
if (isShell) console.log('Rendering the shell!')
}
// src/routes/root.tsx
export default function Root() {
const isShell = useRouter().isShell()
if (isShell) console.log('Rendering the shell!')
}
Sie können diesen booleschen Wert verwenden, um UI bedingt basierend darauf zu rendern, ob die aktuelle Route eine Shell ist oder nicht. Beachten Sie jedoch, dass der Router nach dem Hydrieren der Shell sofort zur ersten Route navigiert und isShell() false zurückgibt. Dies kann zu Flackern von ungestyltem Inhalt führen, wenn es nicht richtig gehandhabt wird.
Da die Shell mit dem SSR-Build Ihrer Anwendung vorab gerendert wird, werden alle loader oder serverseitigen Funktionalitäten, die auf Ihrer Stammroute definiert sind, während des Vorab-Rendering-Prozesses ausgeführt und die Daten werden in die Shell aufgenommen.
Das bedeutet, dass Sie dynamische Daten in Ihrer Shell verwenden können, indem Sie einen loader oder serverseitige Funktionalitäten verwenden.
// src/routes/__root.tsx
export const RootRoute = createRootRoute({
loader: async () => {
return {
name: 'Tanner',
}
},
component: Root,
})
export default function Root() {
const { name } = useLoaderData()
return (
<html>
<body>
<h1>Hello, {name}!</h1>
<Outlet />
</body>
</html>
)
}
// src/routes/__root.tsx
export const RootRoute = createRootRoute({
loader: async () => {
return {
name: 'Tanner',
}
},
component: Root,
})
export default function Root() {
const { name } = useLoaderData()
return (
<html>
<body>
<h1>Hello, {name}!</h1>
<Outlet />
</body>
</html>
)
}
Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.