SPA-Modus

Was zum Teufel ist SPA-Modus?

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.

Warum Start ohne SSR verwenden?

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**.

Vorteile des SPA-Modus

  • Einfacher bereitzustellen - Ein CDN, das statische Assets bereitstellen kann, ist alles, was Sie brauchen.
  • Günstiger im Hosting - CDNs sind im Vergleich zu Lambda-Funktionen oder langlebigen Prozessen günstig.
  • Nur Client-seitig ist einfacher - Kein SSR bedeutet weniger, was bei Hydration, Rendering und Routing schiefgehen kann.

Nachteile des SPA-Modus

  • Langsamere Zeit bis zum vollständigen Inhalt - Die Zeit bis zum vollständigen Inhalt ist länger, da das gesamte JS heruntergeladen und ausgeführt werden muss, bevor etwas unterhalb der Shell gerendert werden kann.
  • Weniger SEO-freundlich - Roboter, Crawler und Link-Unfurler *können* Schwierigkeiten haben, Ihre Anwendung zu indizieren, es sei denn, sie sind so konfiguriert, dass sie JS ausführen und Ihre Anwendung innerhalb einer angemessenen Zeit rendern kann.

Wie funktioniert es?

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

  • Vorab-Rendering nur der Stammroute Ihrer Anwendung
  • Wo Ihre Anwendung normalerweise Ihre übereinstimmenden Routen rendern würde, wird stattdessen die konfigurierte ausstehende Fallback-Komponente Ihres Routers gerendert.
  • Das resultierende HTML wird in einer statischen HTML-Seite namens /_shell.html (konfigurierbar) gespeichert.
  • Standardmäßig sind Rewrites konfiguriert, um alle 404-Anfragen zur SPA-Modus-Shell umzuleiten.

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.

Konfiguration des SPA-Modus

Um den SPA-Modus zu konfigurieren, gibt es einige Optionen, die Sie den Optionen Ihres Start-Plugins hinzufügen können.

tsx
// vite.config.ts
export default defineConfig({
  plugins: [
    TanStackStart({
      spa: {
        enabled: true,
      },
    }),
  ],
})
// vite.config.ts
export default defineConfig({
  plugins: [
    TanStackStart({
      spa: {
        enabled: true,
      },
    }),
  ],
})

Notwendige Weiterleitungen verwenden

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:

  1. Stellen Sie sicher, dass statische Assets immer bereitgestellt werden, falls sie existieren, z.B. /about.html. Dies ist normalerweise das Standardverhalten der meisten CDNs.
  2. (Optional) Erlauben Sie bestimmten Unterpfaden, an dynamische Server-Handler weitergeleitet zu werden, z.B. /api/** (Mehr dazu unten).
  3. Stellen Sie sicher, dass alle 404-Anfragen zur SPA-Shell umgeschrieben werden, z.B. ein Catch-All-Redirect zu /_shell.html (oder wenn Sie den Ausgabe-Pfad Ihrer Shell benutzerdefiniert haben, verwenden Sie diesen stattdessen).

Beispiel für einfache Weiterleitungen

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

Serverfunktionen und Serverrouten zulassen

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

Shell-Maskenpfad

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.

tsx
// vite.config.ts
export default defineConfig({
  plugins: [
    tanstackStart({
      spa: {
        maskPath: '/app',
      },
    }),
  ],
})
// vite.config.ts
export default defineConfig({
  plugins: [
    tanstackStart({
      spa: {
        maskPath: '/app',
      },
    }),
  ],
})

Optionen für Vorab-Rendering

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:

  • outputPath: /_shell.html
  • crawlLinks: false
  • retryCount: 0

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.

tsx
// 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,
        },
      },
    }),
  ],
})

Benutzerdefinierte Darstellung im SPA-Modus

Die Anpassung der HTML-Ausgabe der SPA-Shell kann nützlich sein, wenn Sie

  • Generische Head-Tags für SPA-Routen bereitstellen möchten.
  • Eine benutzerdefinierte ausstehende Fallback-Komponente bereitstellen möchten.
  • Buchstäblich alles am HTML, CSS und JS der Shell ändern möchten.

Um diesen Prozess zu vereinfachen, ist eine isShell()-Funktion auf der router-Instanz verfügbar.

tsx
// 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.

Dynamische Daten in Ihrer Shell

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.

tsx
// 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>
  )
}
Unsere Partner
Code Rabbit
Netlify
Neon
Clerk
Convex
Sentry
Prisma
Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.

Bytes abonnieren

Ihre wöchentliche Dosis JavaScript-Nachrichten. Jeden Montag kostenlos an über 100.000 Entwickler geliefert.

Bytes

Kein Spam. Jederzeit kündbar.