Hier erfährst du, wie die Komponenten von RenderingNG eingerichtet werden und wie die Rendering-Pipeline durch sie hindurchfließt.
Auf der obersten Ebene sind das:
- Inhalte in Pixel auf dem Bildschirm rendern
- Visuelle Effekte von einem Zustand in einen anderen animieren.
- Scrollen Sie als Reaktion auf die Eingabe.
- Leiten Sie Eingaben effizient an die richtigen Stellen weiter, damit Entwicklerscripts und andere Teilsysteme reagieren können.
Der zu rendernde Inhalt besteht aus einem Frame-Baum für jeden Browsertab sowie der Browseroberfläche. Außerdem einen Stream von Roheingabeereignissen von Touchscreens, Mäusen, Tastaturen und anderen Hardwaregeräten.
Jeder Frame enthält:
- DOM-Status
- CSS
- Canvases
- Externe Ressourcen wie Bilder, Videos, Schriftarten und SVG
Ein Frame besteht aus einem HTML-Dokument und seiner URL. Eine Webseite, die in einem Browsertab geladen wird, hat einen Frame der obersten Ebene, untergeordnete Frames für jeden Iframe im übergeordneten Dokument und deren rekursive Iframe-Abkömmlinge.
Ein visueller Effekt ist ein grafischer Vorgang, der auf eine Bitmap angewendet wird, z. B. Scrollen, Transformieren, Zuschneiden, Filtern, Deckkraft oder Überblenden.
Architekturkomponenten
In RenderingNG werden diese Aufgaben logisch auf mehrere Phasen und Codekomponenten aufgeteilt. Die Komponenten landen in verschiedenen CPU-Prozessen, Threads und Unterkomponenten innerhalb dieser Threads. Jedes spielt eine wichtige Rolle bei der Gewährleistung von Zuverlässigkeit, skalierbarer Leistung und Erweiterbarkeit für alle Webinhalte.
Pipelinestruktur für das Rendern
Das Rendering erfolgt in einer Pipeline mit einer Reihe von Phasen und Artefakten, die dabei erstellt werden. Jede Phase steht für Code, der eine klar definierte Aufgabe beim Rendering ausführt. Die Artefakte sind Datenstrukturen, die Eingaben oder Ausgaben der Phasen sind.
Die Phasen:
- Animieren:Berechnete Stile ändern und Property-Abbildungen im Zeitverlauf basierend auf deklarativen Zeitachsen verändern.
- Stil:Hiermit wird CSS auf das DOM angewendet und berechnete Stile erstellt.
- Layout: Bestimmt die Größe und Position von DOM-Elementen auf dem Bildschirm und erstellt den unveränderlichen Fragmentbaum.
- Vorab-Rendering:Berechnen Sie die Property-Bäume und invalidate
- Scrollen:Aktualisieren Sie den Scroll-Offset von Dokumenten und scrollbaren DOM-Elementen, indem Sie Attributbäume ändern.
- Paint:Berechnet eine Displayliste, die beschreibt, wie GPU-Texturkacheln aus dem DOM gerastert werden.
- Commit: Kopiert die Property-Bäume und die Displayliste in den Compositor-Thread.
- Ebenen: Die Anzeigeliste wird in eine Liste mit zusammengesetzten Ebenen für unabhängige Rasterung und Animation aufgeteilt.
- Raster, Dekodieren und Paint Worklets:Hier werden Displaylisten, codierte Bilder und Paint Worklet-Code in GPU-Textur-Kacheln umgewandelt.
- Aktivieren:Erstellen Sie einen Compositor-Frame, der angibt, wie GPU-Kacheln zusammen mit visuellen Effekten auf dem Bildschirm gezeichnet und positioniert werden sollen.
- Zusammenfassen:Hier werden die Frames aller sichtbaren Compositoren zu einem einzigen globalen Compositor-Frame kombiniert.
- Zeichnen:Der aggregierte Renderer-Frame wird auf der GPU ausgeführt, um Pixel auf dem Bildschirm zu erstellen.
Phasen der Rendering-Pipeline können übersprungen werden, wenn sie nicht erforderlich sind. So können beispielsweise Animationen von visuellen Effekten und Scrollen das Layout überspringen, vormalen und malen. Deshalb sind Animation und Scrollen im Diagramm mit gelben und grünen Punkten markiert. Wenn Layout, Vorab-Paint und Paint für visuelle Effekte übersprungen werden können, können sie vollständig im Compositor-Thread ausgeführt und der Hauptthread übersprungen werden.
Das Browser-UI-Rendering wird hier nicht direkt dargestellt, kann aber als vereinfachte Version dieser Pipeline betrachtet werden. Tatsächlich wird bei der Implementierung viel Code gemeinsam genutzt. Video (auch nicht direkt dargestellt) wird in der Regel mit unabhängigem Code gerendert, der Frames in GPU-Texturkacheln decodiert, die dann in Frames des Compositors und in den Zeichenschritt eingebunden werden.
Prozess- und Threadstruktur
CPU-Prozesse
Durch die Verwendung mehrerer CPU-Prozesse wird eine Leistungs- und Sicherheitsisolierung zwischen Websites und dem Browserstatus sowie eine Stabilitäts- und Sicherheitsisolierung von der GPU-Hardware erreicht.
- Der Rendering-Prozess rendert, animiert, scrollt und leitet Eingaben für eine einzelne Kombination aus Website und Tab weiter. Es gibt mehrere Rendering-Prozesse.
- Der Browserprozess rendert, animiert und leitet Eingaben für die Browser-Benutzeroberfläche (einschließlich Adressleiste, Tabtitel und Symbole) weiter und leitet alle verbleibenden Eingaben an den entsprechenden Renderprozess weiter. Es gibt einen Browserprozess.
- Der Viz-Prozess aggregiert das Compositing aus mehreren Rendering-Prozessen sowie dem Browserprozess. Die Rasterung und Zeichnung erfolgt mit der GPU. Es gibt einen Viz-Prozess.
Websites werden immer in verschiedenen Rendering-Prozessen gerendert.
Mehrere Browser-Tabs oder -Fenster derselben Website werden in der Regel in verschiedenen Rendering-Prozessen ausgeführt, es sei denn, die Tabs sind miteinander verknüpft, z. B. wenn einer den anderen öffnet. Bei starkem Arbeitsspeichermangel auf dem Computer kann Chromium mehrere Tabs derselben Website in denselben Rendering-Prozess aufnehmen, auch wenn sie nicht miteinander in Beziehung stehen.
Innerhalb eines einzelnen Browsertabs befinden sich Frames von verschiedenen Websites immer in unterschiedlichen Renderingprozessen, Frames von derselben Website jedoch immer im selben Renderingprozess. Aus Sicht des Renderings besteht der wichtige Vorteil mehrerer Renderingprozesse darin, dass iframes und Tabs auf der Website leistungsmäßig voneinander isoliert sind. Außerdem können Ursprünge eine noch stärkere Isolierung aktivieren.
Es gibt genau einen Viz-Prozess für das gesamte Chromium, da normalerweise nur eine GPU und ein Bildschirm zum Zeichnen vorhanden sind.
Wenn Sie die Visualisierung in einem eigenen Prozess ausführen, ist die Stabilität bei Fehlern in GPU-Treibern oder der Hardware höher. Außerdem eignet es sich gut für die Sicherheitsisolierung, was für GPU-APIs wie Vulkan und Sicherheit im Allgemeinen wichtig ist.
Da der Browser viele Tabs und Fenster haben kann und in allen die Pixel der Browser-Benutzeroberfläche gezeichnet werden müssen, fragen Sie sich vielleicht, warum es genau einen Browserprozess gibt. Der Grund dafür ist, dass nur einer von ihnen jeweils im Fokus ist. Tatsächlich werden nicht sichtbare Browser-Tabs meistens deaktiviert und der gesamte GPU-Speicher wird freigegeben. Allerdings werden auch komplexe Funktionen zum Rendern der Browser-Benutzeroberfläche zunehmend in Rendering-Prozesse implementiert (WebUI). Dies geschieht nicht aus Gründen der Leistungsisolierung, sondern um die Benutzerfreundlichkeit der Web-Rendering-Engine von Chromium zu nutzen.
Auf älteren Android-Geräten werden der Rendering- und der Browserprozess gemeinsam verwendet, wenn sie in einem WebView verwendet werden. Dies gilt nicht allgemein für Chromium auf Android, sondern nur für WebView. Bei WebView wird der Browserprozess auch mit der App geteilt, in die er eingebettet ist. WebView hat nur einen einzigen Rendering-Prozess.
Manchmal gibt es auch einen Dienstprogrammprozess zum Decodieren geschützter Videoinhalte. Dieser Prozess ist in den vorherigen Diagrammen nicht dargestellt.
Unterhaltungen
Threads tragen dazu bei, trotz langsamer Aufgaben, Pipeline-Parallelisierung und Mehrfachpufferung eine Leistungsisolierung und Reaktionsfähigkeit zu erreichen.
- Der Hauptthread führt Scripts, den Rendering-Ereignis-Loop, den Dokumentlebenszyklus, Treffertests, die Script-Ereignisweitergabe und das Parsen von HTML, CSS und anderen Datenformaten aus.
- Hilfsfunktionen für den Haupt-Thread führen Aufgaben wie das Erstellen von Bild-Bitmaps und Blobs aus, die codiert oder decodiert werden müssen.
- Webworker skript ausführen und einen Rendering-Ereignis-Loop für OffscreenCanvas.
- Der Compositor-Thread verarbeitet Eingabeereignisse, führt Scroll- und Animationsaktionen für Webinhalte aus, berechnet die optimale Schichtung von Webinhalten und koordiniert die Bilddekodierung, Paint-Worklets und Rasteraufgaben.
- Compositor-Thread-Hilfsprogramme koordinieren Viz-Rasteraufgaben und führen Bilddekodierungsaufgaben, Paint-Worklets und Fallback-Raster aus.
- Medien-, Demuxer- oder Audioausgabe-Threads decodieren, verarbeiten und synchronisieren Video- und Audiostreams. Denken Sie daran, dass die Videoverarbeitung parallel zur Haupt-Rendering-Pipeline ausgeführt wird.
Die Trennung des Haupt- und des Kompositions-Threads ist entscheidend für die Leistungsisolation von Animationen und Scrollen von der Arbeit des Hauptthreads.
Es gibt nur einen Haupt-Thread pro Renderprozess, auch wenn mehrere Tabs oder Frames von derselben Website im selben Prozess landen können. Es gibt jedoch eine Leistungsisolierung von Arbeiten, die in verschiedenen Browser-APIs ausgeführt werden. So wird beispielsweise die Generierung von Bild-Bitmaps und -Blobs in der Canvas API in einem Hilfsthread des Hauptthreads ausgeführt.
Ebenso gibt es nur einen einzigen Compositor-Thread pro Rendering-Prozess. Es ist im Allgemeinen kein Problem, dass es nur einen gibt, da alle wirklich teuren Vorgänge im Compositor-Thread entweder an Compositor-Arbeitsthreads oder an den Viz-Prozess delegiert werden. Diese Arbeit kann parallel zum Routing von Eingaben, zum Scrollen oder zur Animation ausgeführt werden. Compositor-Worker-Threads koordinieren Aufgaben, die im Viz-Prozess ausgeführt werden. Die allgemeine GPU-Beschleunigung kann jedoch aus Gründen, die außerhalb der Kontrolle von Chromium liegen, wie z. B. Treiberfehlern, fehlschlagen. In diesen Fällen führt der Worker-Thread die Arbeit im Fallback-Modus auf der CPU aus.
Die Anzahl der Compositor-Worker-Threads hängt von den Funktionen des Geräts ab. So werden auf Desktop-Computern in der Regel mehr Threads verwendet, da sie mehr CPU-Kerne haben und weniger Akku-Einschränkungen als Mobilgeräte haben. Dies ist ein Beispiel für die Skalierung nach oben und unten.
Die Architektur des Renderprozesses mit Threads ist eine Anwendung von drei verschiedenen Optimierungsmustern:
- Hilfsthreads: Senden langlaufender Teilaufgaben an zusätzliche Threads, damit der übergeordnete Thread für andere, gleichzeitige Anfragen reaktionsfähig bleibt. Der Haupt- und der Hilfs-Compositor-Thread sind gute Beispiele für diese Technik.
- Mehrere Puffer: Zuvor gerenderte Inhalte werden während des Renderns neuer Inhalte angezeigt, um die Rendering-Latenz zu verbergen. Der Compositor-Thread verwendet diese Technik.
- Pipeline-Parallelisierung:Die Rendering-Pipeline wird an mehreren Stellen gleichzeitig ausgeführt. So können Scrollen und Animation schnell erfolgen. Selbst wenn ein Rendering-Update des Hauptthreads ausgeführt wird, können Scrollen und Animation parallel ausgeführt werden.
Browserprozess
- Der Render- und Komposition-Thread reagiert auf Eingaben in der Browser-Benutzeroberfläche, leitet andere Eingaben an den richtigen Renderprozess weiter und legt die Browser-Benutzeroberfläche aus und malt sie.
- Die Render- und Kompositions-Thread-Hilfsfunktionen führen Aufgaben zur Bilddekodierung und Fallback-Raster- oder Dekodierung aus.
Der Render- und der Komposition-Thread des Browserprozesses ähneln dem Code und der Funktionalität eines Renderprozesses, mit der Ausnahme, dass der Haupt- und der Komposition-Thread zu einem einzigen Thread kombiniert werden. In diesem Fall ist nur ein Thread erforderlich, da keine Leistungsisolierung von langen Aufgaben im Hauptthread erforderlich ist, da es keine gibt.
Viz-Prozess
- Der GPU-Hauptthread rastert Displaylisten und Videoframes in GPU-Texturkacheln und zeichnet Renderer-Frames auf dem Bildschirm.
- Der Display-Compositor-Thread aggregiert und optimiert das Compositing aus jedem Rendering-Prozess sowie dem Browserprozess in einem einzigen Compositor-Frame für die Darstellung auf dem Bildschirm.
Rasterung und Zeichnen erfolgen in der Regel im selben Thread, da beide auf GPU-Ressourcen angewiesen sind und es schwierig ist, die GPU zuverlässig mehrmals zu verwenden. Ein einfacherer mehrstufiger Zugriff auf die GPU ist eine der Motivationen für die Entwicklung des neuen Vulkan-Standards. Auf Android WebView gibt es aufgrund der Einbettung von WebViews in eine native App einen separaten Render-Thread auf Betriebssystemebene für das Zeichnen. Andere Plattformen werden in Zukunft wahrscheinlich auch einen solchen Thread haben.
Der Display-Compositor befindet sich in einem anderen Thread, da er jederzeit reaktionsschnell sein und keine möglichen Ursachen für eine Verlangsamung des GPU-Hauptthreads blockieren darf. Eine Ursache für eine Verlangsamung des GPU-Hauptthreads sind Aufrufe von nicht Chromium-Code, z. B. anbieterspezifische GPU-Treiber, die auf schwer vorhersehbare Weise langsam sein können.
Komponentenstruktur
Innerhalb jedes Haupt- oder Kompositionsleiters des Rendering-Prozesses gibt es logische Softwarekomponenten, die strukturiert miteinander interagieren.
Hauptthread-Komponenten des Renderprozesses
Im Blink-Renderer:
- Das Fragment des lokalen Frame-Baums stellt den Baum der lokalen Frames und das DOM innerhalb der Frames dar.
- Die Komponente DOM- und Canvas-APIs enthält Implementierungen all dieser APIs.
- Der Document Lifecycle Runner führt die Schritte der Rendering-Pipeline bis einschließlich des Commit-Schritts aus.
- Die Komponente Eingabeereignis-Treffertests und ‑Auslösung führt Treffertests durch, um herauszufinden, auf welches DOM-Element ein Ereignis ausgerichtet ist, und führt die Algorithmen und Standardverhalten für die Eingabeereignisauslösung aus.
Der Scheduler und Runner für die Ereignisschleife entscheidet, was und wann in der Ereignisschleife ausgeführt wird. Das Rendering wird mit einer Taktfrequenz geplant, die dem Display des Geräts entspricht.
Fragmente des lokalen Frame-Baums sind etwas kompliziert. Ein Frame-Baum besteht aus der Hauptseite und ihren untergeordneten IFrames, rekursiv. Ein Frame ist lokal für einen Renderingprozess, wenn er in diesem Prozess gerendert wird. Andernfalls ist er remote.
Sie können sich vorstellen, Frames entsprechend ihrem Rendering-Prozess zu färben. Auf dem vorherigen Bild sind die grünen Kreise alle Frames in einem Rendering-Prozess, die orangefarbenen in einem zweiten und der blaue in einem dritten.
Ein lokales Frame-Baumfragment ist eine verbundene Komponente derselben Farbe in einem Frame-Baum. Auf dem Bild sind vier lokale Framebäume zu sehen: zwei für Standort A, einer für Standort B und einer für Standort C. Jeder lokale Frame-Baum erhält eine eigene Blink-Rendering-Komponente. Der Blink-Renderer eines lokalen Frame-Baums befindet sich möglicherweise im selben Rendering-Prozess wie andere lokale Frame-Bäume. Sie wird durch die Art und Weise bestimmt, wie Rendering-Prozesse ausgewählt werden, wie oben beschrieben.
Struktur des Rendering-Prozess-Compositor-Threads
Die Komponenten des Renderer-Prozess-Compositors umfassen:
- Ein Datenhandler, der eine Liste der zusammengesetzten Ebenen, Anzeigelisten und Property-Bäume verwaltet.
- Ein Lebenszyklus-Ausführer, der die Schritte „animieren“, „scrollen“, „zusammensetzen“, „rastern“ und „decodieren“ und „aktivieren“ der Rendering-Pipeline ausführt. Denken Sie daran, dass „animieren“ und „scrollen“ sowohl im Hauptthread als auch im Renderer erfolgen können.
- Ein Eingabe- und Treffertest-Handler führt die Eingabeverarbeitung und den Treffertest mit der Auflösung der zusammengesetzten Ebenen aus, um zu ermitteln, ob Scroll-Gesten im Compositor-Thread ausgeführt werden können und auf welchen Renderprozess die Treffertests ausgerichtet werden sollen.
Beispielarchitektur in der Praxis
In diesem Beispiel gibt es drei Tabs:
Tab 1: foo.com
<html>
<iframe id=one src="foo.com/other-url"></iframe>
<iframe id=two src="bar.com"></iframe>
</html>
Tab 2: bar.com
<html>
…
</html>
Tab 3: baz.com
html
<html>
…
</html>
Die Prozess-, Thread- und Komponentenstruktur für diese Tabs sieht so aus:
Sehen wir uns jeweils ein Beispiel für die vier Hauptaufgaben des Renderings an. Zur Erinnerung:
- Inhalte in Pixel auf dem Bildschirm rendern.
- Visuelle Effekte auf den Inhalten von einem Zustand in einen anderen animieren.
- Scrollen Sie als Reaktion auf die Eingabe.
- Leiten Sie Eingaben effizient an die richtigen Stellen weiter, damit Entwicklerscripts und andere Teilsysteme reagieren können.
So rendern Sie das geänderte DOM für Tab 1:
- Ein Entwicklerskript ändert das DOM im Renderprozess für foo.com.
- Der Blink-Renderer teilt dem Compositor mit, dass ein Rendering erforderlich ist.
- Der Compositor teilt Viz mit, dass ein Rendern erforderlich ist.
- Viz signalisiert dem Compositor den Beginn des Renderings.
- Der Renderer leitet das Startsignal an den Blink-Renderer weiter.
- Der Event-Loop-Ausführer des Hauptthreads führt den Dokumentlebenszyklus aus.
- Der Hauptthread sendet das Ergebnis an den Compositor-Thread.
- Der Compositor-Ereignisschleifen-Ausführer führt den Compositing-Lebenszyklus aus.
- Alle Rasteraufgaben werden an Viz für Raster gesendet (es gibt oft mehr als eine dieser Aufgaben).
- Viz rastert Inhalte auf der GPU.
- Viz bestätigt den Abschluss der Rasteraufgabe. Hinweis: Chromium wartet oft nicht, bis der Raster fertig ist, sondern verwendet stattdessen ein sogenanntes Synchronisierungstoken, das von Rasteraufgaben aufgelöst werden muss, bevor Schritt 15 ausgeführt wird.
- Ein Compositor-Frame wird an Viz gesendet.
- Viz aggregiert die Frames des Renderers für den Renderprozess von foo.com, den Iframe-Renderprozess von bar.com und die Browser-Benutzeroberfläche.
- Viz plant eine Auslosung.
- Viz zeichnet den zusammengefassten Renderer-Frame auf dem Bildschirm.
So animate Sie einen CSS-Transformationsübergang auf Tab 2:
- Der Compositor-Thread für den Renderprozess von bar.com tickt eine Animation in seinem Compositor-Ereignis-Loop, indem er die vorhandenen Property-Bäume mutiert. Dadurch wird der Lebenszyklus des Renderers noch einmal durchlaufen. Raster- und Dekodierungsaufgaben können vorkommen, werden hier aber nicht dargestellt.
- Ein Compose-Frame wird an Viz gesendet.
- Viz aggregiert die Frames des Renderers für den Renderingprozess von foo.com, den Renderingprozess von bar.com und die Browser-Benutzeroberfläche.
- Viz plant eine Auslosung.
- Viz zeichnet den zusammengefassten Renderer-Frame auf dem Bildschirm.
So scrollen Sie auf dem Tab 3 durch die Webseite:
- Eine Sequenz von
input
-Ereignissen (Maus, Touch oder Tastatur) wird an den Browserprozess gesendet. - Jedes Ereignis wird an den Kompositionsleiter-Thread des Renderprozesses von baz.com weitergeleitet.
- Der Compositor bestimmt, ob der Hauptthread über das Ereignis informiert werden muss.
- Das Ereignis wird gegebenenfalls an den Hauptthread gesendet.
- Der Hauptthread löst
input
-Ereignis-Listener (pointerdown
,touchstar
,pointermove
,touchmove
oderwheel
) aus, um zu prüfen, ob ListenerpreventDefault
für das Ereignis aufrufen. - Der Hauptthread gibt zurück, ob
preventDefault
an den Compositor übergeben wurde. - Andernfalls wird das Eingabeereignis an den Browserprozess zurückgesendet.
- Der Browserprozess wandelt es in eine Scrollgeste um, indem er es mit anderen aktuellen Ereignissen kombiniert.
- Die Scrollgeste wird noch einmal an den Thread des Rendering-Prozess-Komponisten von baz.com gesendet.
- Dort wird das Scrollen angewendet und der Compositor-Thread für den Rendervorgang von bar.com tickt eine Animation in seinem Compositor-Ereignis-Loop.
Dadurch wird der Scroll-Offset in den Property-Bäumen verändert und der Lebenszyklus des Renderers wird noch einmal ausgeführt.
Außerdem wird der Hauptthread angewiesen, ein
scroll
-Ereignis auszulösen (nicht hier dargestellt). - Ein Compose-Frame wird an Viz gesendet.
- Viz aggregiert die Frames des Renderers für den Renderingprozess von foo.com, den Renderingprozess von bar.com und die Browser-Benutzeroberfläche.
- Viz plant eine Auslosung.
- Viz zeichnet den zusammengefassten Renderer-Frame auf dem Bildschirm.
So leiten Sie ein click
-Ereignis auf einen Hyperlink im iFrame 2 auf Tab 1 weiter:
- Ein
input
-Ereignis (Maus, Touch oder Tastatur) wird an den Browserprozess gesendet. Es wird ein ungefährer Treffertest durchgeführt, um festzustellen, ob der iFrame-Rendering-Prozess von bar.com den Klick erhalten soll. Wenn ja, wird er dorthin gesendet. - Der Compositor-Thread für bar.com leitet das
click
-Ereignis an den Hauptthread für bar.com weiter und plant einen Rendering-Ereignisschleifen-Task zur Verarbeitung. - Der Eingabeereignis-Prozessor für den Hauptthread von bar.com führt Treffertests durch, um zu ermitteln, auf welches DOM-Element im Iframe geklickt wurde, und löst ein
click
-Ereignis für Scripts aus. Wenn keinpreventDefault
erkannt wird, wird der Hyperlink aufgerufen. - Beim Laden der Zielseite des Hyperlinks wird der neue Status gerendert. Dabei werden ähnliche Schritte wie im vorherigen Beispiel „Rendern des geänderten DOM“ ausgeführt. (Diese nachfolgenden Änderungen sind hier nicht dargestellt.)
Fazit
Es kann viel Zeit in Anspruch nehmen, sich das Rendering einzuprägen.
Das Wichtigste ist, dass die Rendering-Pipeline durch sorgfältige Modularisierung und Liebe zum Detail in eine Reihe von eigenständigen Komponenten unterteilt wurde. Diese Komponenten wurden dann auf parallele Prozesse und Threads aufgeteilt, um die skalierbare Leistung und die Erweiterbarkeit zu maximieren.
Jede Komponente spielt eine wichtige Rolle bei der Leistung und den Funktionen moderner Webanwendungen.
Weiter unten finden Sie Informationen zu den wichtigsten Datenstrukturen, die für RenderingNG genauso wichtig sind wie Codekomponenten.
Illustrationen von Una Kravets