Code Splitting Strategien jenseits der Basics
AI generated
JS
() =>
JavaScript · Code Splitting · Bundling · Web Performance
Code Splitting Strategien jenseits der Basics
Route, Component und Vendor Splits richtig kombinieren

Route Splitting ist der Standardeinstieg, aber die spürbare Verbesserung liegt oft eine Ebene tiefer. Component Level Splits, gezieltes Vendor Chunking und Preload Hints entscheiden gemeinsam, ob Code Splitting im Bundle Report gut aussieht oder ob es tatsächlich die Ladezeit für Nutzer verkürzt.

18 Min. Lesezeit Code Splitting · Vendor Chunking · Preload · Prefetch Vite · Webpack · Rollup 2026

1. Warum Route Splitting allein oft enttäuscht

Code Splitting auf Routenebene ist der übliche erste Schritt: jede Seite einer Anwendung bekommt ihr eigenes Bundle, das erst beim Navigieren geladen wird. Das reduziert das initiale Bundle spürbar, stößt aber schnell an Grenzen, sobald einzelne Routen selbst groß werden, etwa ein Dashboard mit Diagrammen, einem Rich Text Editor oder einer Kartenkomponente. Route basiertes Code Splitting behandelt eine ganze Seite als atomare Einheit, obwohl in Wirklichkeit nur ein Bruchteil des Codes beim ersten Rendern tatsächlich gebraucht wird.

Das Problem zeigt sich besonders bei Anwendungen mit wenigen, aber funktional umfangreichen Routen. Eine einzelne Produktseite in einem Shop kann leicht mehrere hundert Kilobyte an JavaScript für Bewertungen, Empfehlungen, Zoom Funktionen und Varianten Auswahl enthalten, obwohl beim ersten Laden nur Titel, Preis und Bild sichtbar sein müssen. Reines Code Splitting nach Route verpasst genau diese Gelegenheit, weil es die interne Struktur einer Seite nicht berücksichtigt.

Die Lösung liegt darin, Code Splitting als mehrschichtige Strategie zu verstehen: Route Splitting als grobe erste Ebene, Component Level Splitting für einzelne teure Bereiche innerhalb einer Route, und Vendor Chunking für gemeinsame Abhängigkeiten über Routen hinweg. Erst das Zusammenspiel dieser drei Ebenen macht aus Code Splitting eine Technik, die tatsächlich die wahrgenommene Ladezeit senkt, statt nur die Zahl der Bundle Dateien zu erhöhen.

2. Component Level Splitting: gezielt statt pauschal

Component Level Code Splitting lädt einzelne, klar abgegrenzte Komponenten erst dann, wenn sie tatsächlich gebraucht werden, unabhängig von der Route, in der sie vorkommen. Typische Kandidaten sind Modals, die erst nach einer Nutzerinteraktion erscheinen, Rich Text Editoren, die nur im Bearbeitungsmodus benötigt werden, oder komplexe Diagrammbibliotheken, die nur bei bestimmten Nutzerrollen sichtbar sind. Dynamic Import mit import Funktion statt statischem Import ist die technische Grundlage für dieses Muster.

Der Unterschied zu Route Splitting: Component Level Splitting reagiert auf Nutzerverhalten, nicht auf Navigation. Ein Modal, das über einen Button geöffnet wird, muss sein Bundle erst dann laden, wenn der Klick tatsächlich erfolgt, nicht bereits beim ersten Rendern der Seite. Diese Verzögerung ist für Nutzer meist unsichtbar, weil zwischen Klick und sichtbarem Modal sowieso eine kurze Interaktionszeit vergeht, in der das kleine, gezielte Bundle geladen werden kann.

Ein häufiger Fehler beim Component Level Splitting ist, zu viele kleine Komponenten separat zu splitten, ohne die daraus resultierende Anzahl an Netzwerk Requests zu bedenken. Jeder zusätzliche dynamische Import erzeugt einen eigenen Request, und bei HTTP/1.1 oder schlechter Verbindungsqualität summiert sich der Overhead pro Request messbar. Component Level Code Splitting lohnt sich vor allem für Komponenten, die selbst mindestens einige Dutzend Kilobyte groß sind und selten sofort benötigt werden.


// Component level splitting: only load the rich text editor
// when the user actually opens the edit mode
import { lazy, Suspense, useState } from "react";

const RichTextEditor = lazy(() => import("./RichTextEditor.jsx"));

function ProductDescription({ text }) {
  const [isEditing, setIsEditing] = useState(false);

  if (!isEditing) {
    return (
      <div>
        <p>{text}</p>
        <button onClick={() => setIsEditing(true)}>Edit</button>
      </div>
    );
  }

  return (
    <Suspense fallback={<div>Loading editor...</div>}>
      <RichTextEditor initialValue={text} />
    </Suspense>
  );
}

3. Vendor Chunking: gemeinsame Abhängigkeiten strategisch bündeln

Vendor Chunking gruppiert Third Party Abhängigkeiten, die von mehreren Teilen der Anwendung genutzt werden, in eigene, separat cachebare Chunks. Ohne bewusstes Vendor Chunking landet eine große Bibliothek wie eine Datumsverarbeitung oder ein State Management Framework in jedem Chunk, der sie importiert, was zu massiver Code Duplikation über mehrere Bundle Dateien hinweg führt. Moderne Bundler wie Vite und Rollup bieten dafür die manualChunks Konfiguration, die genau steuert, welche Module in welchen Chunk wandern.

Eine bewährte Vendor Chunking Strategie trennt nach Änderungshäufigkeit: Bibliotheken, die sich selten ändern, etwa React oder eine UI Bibliothek, kommen in einen eigenen, langfristig cachebaren Chunk, während der Anwendungscode selbst, der sich bei jedem Deployment ändert, in separaten Chunks bleibt. Dieses Muster maximiert die Cache Trefferquote, weil Nutzer den Vendor Chunk über viele Deployments hinweg aus dem Browser Cache laden können, ohne ihn erneut herunterladen zu müssen.

Ein Risiko beim Vendor Chunking ist die Übergranularität: wenn zu viele kleine Vendor Chunks entstehen, wächst die Anzahl der Requests, ohne dass der Cache Vorteil den zusätzlichen Overhead ausgleicht. Die Praxis zeigt, dass zwei bis vier Vendor Chunks, gruppiert nach thematischer Nähe, meist die beste Balance zwischen Cache Effizienz und Request Anzahl bieten, verglichen mit einem einzigen riesigen Vendor Bundle oder Dutzenden winziger Chunks pro einzelner Bibliothek.


// vite.config.js — deliberate vendor chunking by update frequency
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes("node_modules")) {
            if (id.includes("react") || id.includes("react-dom")) {
              return "vendor-react"; // rarely changes, cache long term
            }
            if (id.includes("chart.js") || id.includes("d3")) {
              return "vendor-charts"; // only needed on dashboard routes
            }
            return "vendor-misc"; // remaining smaller dependencies
          }
        },
      },
    },
  },
};

4. Chunk Granularität: zu viele und zu wenige Chunks vermeiden

Die richtige Chunk Granularität beim Code Splitting ist ein Balanceakt zwischen zwei entgegengesetzten Risiken. Zu wenige, zu große Chunks bedeuten, dass Nutzer weiterhin Code laden, den sie für die aktuelle Ansicht gar nicht brauchen, was den ursprünglichen Zweck von Code Splitting untergräbt. Zu viele, zu kleine Chunks erzeugen dagegen einen Request Overhead, der bei HTTP/2 zwar geringer ausfällt als bei HTTP/1.1, aber niemals vollständig verschwindet, da jeder Chunk weiterhin geparst und ausgeführt werden muss.

Eine praktikable Faustregel: Chunks unter etwa zehn Kilobyte komprimiert bringen selten einen Nettovorteil, weil der Overhead für Verbindung, Parsing und Modul Registrierung den Vorteil der kleineren Downloadgröße aufwiegt. Bundler wie Webpack bieten mit splitChunks minSize und maxSize direkte Konfigurationsoptionen, um diese Grenze zu erzwingen, statt sie dem Zufall der Modul Struktur zu überlassen.

Wichtig ist auch, dass Chunk Granularität nicht statisch für die gesamte Anwendung gilt. Kritische Pfade wie der Checkout Prozess in einem Shop profitieren von größeren, dafür weniger fragmentierten Chunks, um Latenz zu minimieren, während selten genutzte administrative Bereiche von aggressiverem Code Splitting profitieren, weil dort die Ladezeit weniger kritisch, aber die Reduktion des initialen Bundles wichtiger ist.

5. Preload und Prefetch: Splitting mit Voraussicht kombinieren

Code Splitting ohne Preload und Prefetch verschiebt Ladezeit nur, statt sie zu eliminieren: der Nutzer lädt weniger beim ersten Seitenaufruf, muss dafür aber bei jeder Navigation auf einen neuen Chunk warten. Preload Hints mit rel="preload" für Chunks, die mit hoher Wahrscheinlichkeit als Nächstes gebraucht werden, und Prefetch Hints mit rel="prefetch" für wahrscheinliche zukünftige Navigationsziele gleichen diesen Effekt aus, indem sie den Download bereits im Hintergrund starten, bevor der Nutzer tatsächlich klickt.

Moderne Frameworks und Router integrieren diese Hints oft automatisch: ein Link Element, das im sichtbaren Viewport erscheint, kann per Intersection Observer automatisch das zugehörige Route Chunk als Prefetch anfordern, sodass beim tatsächlichen Klick das Bundle bereits im Cache liegt. Diese Technik kombiniert die Vorteile von Code Splitting, kleineres initiales Bundle, mit den Vorteilen eines monolithischen Bundles, keine wahrgenommene Wartezeit bei Navigation.

Vorsicht ist bei aggressivem Prefetching auf Verbindungen mit begrenzter Bandbreite geboten. Die Network Information API erlaubt es, das Verhalten anhand von navigator.connection.effectiveType anzupassen und Prefetching auf langsamen Verbindungen zu deaktivieren, um Datenvolumen zu sparen und die tatsächlich gerade angeforderte Ressource nicht durch konkurrierende Prefetch Requests zu verlangsamen.


// Prefetch a route chunk when its link enters the viewport,
// but skip it on slow connections to avoid wasting bandwidth
function prefetchOnVisible(linkElement, chunkImporter) {
  const connection = navigator.connection;
  if (connection && ["slow-2g", "2g"].includes(connection.effectiveType)) {
    return; // skip prefetching on slow connections
  }

  const observer = new IntersectionObserver((entries) => {
    for (const entry of entries) {
      if (entry.isIntersecting) {
        chunkImporter(); // triggers the dynamic import, browser caches it
        observer.disconnect();
      }
    }
  });

  observer.observe(linkElement);
}

prefetchOnVisible(
  document.querySelector("a[href='/checkout']"),
  () => import("./routes/Checkout.jsx")
);

6. Conditional Loading: Features nach Bedarf statt nach Route

Conditional Loading geht einen Schritt über reines Component Level Splitting hinaus und lädt Code basierend auf Feature Flags, Nutzerrollen oder Geräteeigenschaften, unabhängig von Route oder Interaktion. Ein Beispiel: eine komplexe Barrierefreiheits Erweiterung, die nur für Nutzer mit aktivierten Screen Reader Einstellungen geladen wird, oder eine Admin Toolbar, die nur für Nutzer mit entsprechender Berechtigung überhaupt in das Bundle eingebunden wird.

Diese Form des Code Splitting erfordert, dass die Bedingung für das Laden früh genug im Anwendungslebenszyklus bekannt ist, meist direkt nach der Authentifizierung oder beim initialen Feature Flag Abruf. Ein häufiges Muster ist ein zentraler Feature Loader, der basierend auf einer Konfiguration entscheidet, welche dynamischen Imports überhaupt ausgeführt werden, statt diese Entscheidung über die gesamte Codebasis zu verstreuen.

Der Vorteil von Conditional Loading gegenüber statischem Code Splitting: die Bundle Größe passt sich exakt an die tatsächliche Nutzung an, statt eine für alle Nutzer identische Struktur vorzugeben. Ein Nutzer ohne Admin Rechte lädt niemals auch nur ein Byte der Admin Toolbar, während reines Route Splitting diese Unterscheidung ohne zusätzliche Logik nicht treffen kann.

7. Request Waterfalls: das unsichtbare Risiko von zu viel Splitting

Ein oft übersehenes Risiko von aggressivem Code Splitting sind Request Waterfalls: Chunk A importiert Chunk B, der wiederum Chunk C importiert, und jeder dieser Importe löst einen sequenziellen Netzwerk Request aus, statt parallel geladen zu werden. Bei tief verschachtelten dynamischen Imports kann sich die tatsächliche Ladezeit trotz kleinerer Einzeldateien verlängern, weil die Kette der sequenziellen Requests die Netzwerklatenz vervielfacht statt sie zu reduzieren.

Moderne Bundler erkennen solche Waterfalls teilweise automatisch und fassen tief verschachtelte Chunks zusammen, aber die zuverlässigste Gegenmaßnahme bleibt eine bewusste Analyse der Import Graphen. Tools wie der Rollup Bundle Visualizer oder Webpack Bundle Analyzer stellen die Abhängigkeitsstruktur grafisch dar und zeigen genau, wo tiefe Verschachtelungen entstehen, die für Request Waterfalls anfällig sind.

Ein praktischer Kniff gegen Waterfalls ist explizites Preloading verschachtelter Abhängigkeiten mit der Modulepreload Direktive, die dem Browser erlaubt, alle bekannten Abhängigkeiten eines Chunks parallel statt sequenziell zu laden. Diese Technik verwandelt eine potenzielle Kette sequenzieller Requests in einen einzigen parallelen Ladevorgang, ohne dass die eigentliche Code Splitting Struktur verändert werden muss.

8. Wirkung messen: Bundle Analyzer und reale Ladezeiten

Code Splitting ohne Messung ist Spekulation. Ein Bundle Analyzer zeigt die Größe jedes einzelnen Chunks und dessen Abhängigkeiten, aber die entscheidende Frage ist, wie sich diese Struktur auf reale Ladezeiten auswirkt, nicht nur auf die Dateigröße im Build Report. Metriken wie Time to Interactive und Largest Contentful Paint aus echten Feld Daten zeigen, ob eine Splitting Änderung tatsächlich bei Nutzern ankommt oder nur den Build Report optisch verbessert.

Eine bewährte Vorgehensweise ist, vor und nach einer Code Splitting Änderung ein Recording im Performance Panel zu vergleichen, kombiniert mit einer synthetischen Messung über mehrere Netzwerk und Geräteprofile. Eine Splitting Strategie, die auf einem schnellen Entwickler Rechner mit Glasfaserverbindung gut aussieht, kann auf einem durchschnittlichen Mobilgerät mit 4G Verbindung durch zusätzliche Request Overhead tatsächlich langsamer sein als eine weniger fragmentierte Alternative.

9. Splitting Strategien im Vergleich

Die folgende Tabelle ordnet die vorgestellten Strategien nach ihrem typischen Einsatzgebiet und ihrem Effekt auf initiale Ladezeit gegenüber Navigationsgeschwindigkeit.

Strategie Einsatzgebiet Effekt Risiko
Route Splitting Mehrseitige Anwendungen Grobe Reduktion initiales Bundle Große Routen bleiben unadressiert
Component Splitting Modals, Editoren, schwere Widgets Gezielte Reduktion pro Feature Zu viele Chunks bei Übertreibung
Vendor Chunking Gemeinsame Third Party Abhängigkeiten Hohe Cache Trefferquote Übergranularität erhöht Requests
Conditional Loading Feature Flags, Rollen, Geräte Exakte Anpassung an Nutzung Komplexere Ladelogik nötig
Preload/Prefetch Wahrscheinliche Navigationsziele Keine wahrgenommene Wartezeit Bandbreitenverschwendung ohne Drosselung

Keine dieser Strategien funktioniert isoliert optimal. Erst die Kombination aus Route Splitting als Grundgerüst, Component Splitting für schwere Einzelteile, Vendor Chunking für Cache Effizienz und Preload für Navigationsgeschwindigkeit macht aus Code Splitting eine ganzheitliche Strategie statt einer isolierten Einzelmaßnahme.

Mironsoft

Bundle Optimierung und Code Splitting Architektur

Bundle Größe wirklich in den Griff bekommen?

Wir analysieren eure Bundle Struktur, entwickeln eine mehrschichtige Code Splitting Strategie und messen die Wirkung an realen Ladezeiten, nicht nur am Build Report.

Bundle Analyse

Import Graphen und Chunk Struktur mit Bundle Analyzer aufdecken

Splitting Strategie

Component, Vendor und Conditional Splitting kombiniert umsetzen

Preload Tuning

Prefetch und Preload Hints an Nutzungsmuster anpassen

10. Zusammenfassung

Code Splitting entfaltet sein volles Potenzial erst als mehrschichtige Strategie: Route Splitting bildet das grobe Gerüst, Component Level Splitting adressiert einzelne schwere Bereiche innerhalb einer Route, und Vendor Chunking maximiert die Cache Trefferquote über Deployments hinweg. Preload und Prefetch Hints gleichen die Verzögerung aus, die durch Splitting entsteht, indem sie wahrscheinliche nächste Chunks bereits im Hintergrund laden.

Zwei Fallen bleiben zu vermeiden: zu viele kleine Chunks erzeugen Request Overhead und potenzielle Waterfalls, zu wenige große Chunks untergraben den ursprünglichen Zweck von Code Splitting. Wer die Wirkung jeder Änderung mit Bundle Analyzer und realen Feld Daten misst, statt sich auf den Build Report allein zu verlassen, findet die richtige Balance für die eigene Anwendung und ihre tatsächlichen Nutzer.

Code Splitting jenseits der Basics — Das Wichtigste auf einen Blick

Mehrschichtig denken

Route, Component und Vendor Splitting kombinieren, statt sich auf eine einzige Ebene zu verlassen.

Granularität steuern

Chunks unter zehn Kilobyte bringen selten Nettovorteil, minSize und maxSize aktiv konfigurieren.

Preload mit Bedacht

Prefetch für wahrscheinliche Navigation, aber Netzwerkqualität mit der Network Information API berücksichtigen.

Messung vor Meinung

Bundle Analyzer für Struktur, reale Feld Daten für die tatsächliche Wirkung auf Ladezeiten.

11. FAQ: Code Splitting Strategien

1Warum reicht Route Splitting oft nicht?
Route Splitting behandelt eine Seite als Ganzes, obwohl nur ein Teil beim ersten Rendern gebraucht wird.
2Was ist Component Level Splitting?
Gezieltes Laden einzelner Komponenten erst bei tatsächlichem Bedarf, unabhängig von der Route.
3Wie funktioniert Vendor Chunking?
Third Party Abhängigkeiten in eigene, langfristig cachebare Chunks gruppieren, meist nach Änderungshäufigkeit.
4Wie klein darf ein Chunk sein?
Unter zehn Kilobyte komprimiert bringt selten Nettovorteil wegen Verbindungs- und Parsing-Overhead.
5Preload vs. Prefetch?
Preload für die aktuelle Ansicht mit hoher Priorität, Prefetch für wahrscheinliche zukünftige Navigation mit niedriger Priorität.
6Was ist Conditional Loading?
Code wird nach Feature Flags, Rollen oder Geräteeigenschaften geladen, unabhängig von Route oder Interaktion.
7Was ist ein Request Waterfall?
Sequenzielle statt parallele Ladung verschachtelter Chunks verlängert die Ladezeit trotz kleinerer Dateien.
8Wie vermeide ich Waterfalls?
Modulepreload für bekannte Abhängigkeiten nutzen, Import Graph mit Bundle Visualizer prüfen.
9Wie messe ich die Wirkung?
Bundle Analyzer für Struktur, reale Feld Daten zu Time to Interactive und LCP für die tatsächliche Wirkung.
10Prefetching auf allen Verbindungen aktivieren?
Nein, auf langsamen Verbindungen per Network Information API deaktivieren, um Datenvolumen zu sparen.