Themen:
Beschleunigen der drei Schritte:
Funktionalität der Devtools:
Hervorheben von Komponenten, wenn diese neu gerendert werden:
In den Einstellungen der React Devtools: wähle Highlight updates when components render.
Komponenten erhalten beim Rendering einen färbigen Rahmen (Farbe ändert sich abhängig von der Reder-Frequenz)
Auzeichnen und Analysieren einer Session:
In dem "Profiler"-Tab der Browser Tools:
Begutachten der Profiling-Daten:
Jede User-Aktion (z.B. Klick, Texteingabe) wird als ein sogenannter Commit aufgezeichnet
Commits werden im rechten oberen Eck angezeigt
Details werden durch Anklicken sichtbar
Zahlen in einem Commit-Detail:
TodoApp (3ms of 109ms)
bedeutet:
Farben in einem Commit-Detail:
Farbskala von grün bis gelb zeigt an, wie viel Zeit für einzelne Komponenten aufgewendet wurde - verglichen mit Geschwisterkomponenten
Grau gestreifte Komponenten wurden nicht neu gerendert
Memoisierung = Cachen von zuvor berechneten Resultaten
Anwendungen in React:
Beispiel ohne Memoisierung:
const [todos, setTodos] = useState([]);
const numActiveTodos = todos.filter(
(todo) => !todo.completed
).length;
Mit Memoisierung:
const [todos, setTodos] = useState([]);
const numActiveTodos = useMemo(
// function to recompute value
() => todos.filter((todo) => !todo.completed).length,
// array of dependencies
[todos]
);
Berechnung wird nur dann neu ausgeführt, wenn sich eine Abhängigkeit in dem Array ändert
Bemerkung:
Wenn das Rendering einer Komponente das gleiche ist wie zuvor, geschieht das Re-Rendering im Allgemeinen schon schnell, da React nur des virtuelle DOM neu berechnet (und das "echte" DOM nicht ändert)
Oft ist keine weitere Optimierung notwendig
Im Allgemeinen muss eine Komponente nur neu gerendert werden, wenn sich entweder props oder state tatsächlich ändern
was React schon für uns erledigt:
Hooks (state, reducer, context) lösen kein Re-Rendering aus, wenn sich ihr Wert nicht geändert hat
was wir beitragen können:
wenn eine Elternkomponente neu gerendert wird, die Props der Kindkomponente sich aber nicht geändert haben, soll die Kindkomponente nicht neu gerendert werden (Memoisation)
Demo: Komponente rendert sich nur, wenn ihr State sich ändert
function Coin() {
const [coin, setCoin] = useState('heads');
const throwCoin = () => {
setCoin(Math.random() > 0.5 ? 'heads' : 'tails');
};
return (
<div>
{coin}
<button onClick={throwCoin}>throw</button>
<div>last rendering: {new Date().toISOString()}</div>
</div>
);
}
Wenn nur jene Unterkomponenten neu gerendert werden sollen, deren props sich geändert haben:
memo
-FunktionPureComponent
statt Component
optimierte Funktionskomponente:
import { memo } from 'react';
function Rating(props) {
// ...
}
export default memo(Rating);
optimierte Klassenkomponente:
import { PureComponent } from 'react';
class Rating extends PureComponent {
// ...
}
Die Rating
-Komponente wird nicht neu gerendert, wenn ihre Props die gleichen sind wie zuvor:
<Rating stars={prodRating} />
<Rating stars={prodRating} onChange={setProdRating} />
Siehe auch:
Wenn Rating
eine "memoisierte" Komponente ist, welche der folgenden Renderings werden beim Re-Rendering der Elternkomponente auch neu gerendert?
<Rating stars={prodRating} />
<Rating stars={prodRating} onChange={setProdRating} />
<Rating
stars={prodRating}
onChange={(newRating) => setProdRating(newRating)}
/>
<Rating
stars={prodRating}
onChange={(newRating) => setProdRating(newRating)}
/>
Die Pfeilfunktion wäre bei jedem angeforderten Rendering ein anderes Objekt
Lösungen:
dispatch
-Funktion statt neu definierter EventhandlerMemoisierung von Eventhandlern:
function TodoApp() {
const [todos, setTodos] = useState([]);
const deleteTodoA = useMemo(
() => (id) => {
setTodos((todos) => todos.filter((t) => t.id !== id));
},
[]
);
const deleteTodoB = useCallback((id) => {
setTodos((todos) => todos.filter((t) => t.id !== id));
}, []);
}
Beim erneuten Rendern einer React-Komponente: Resultate werden nicht direkt and den Browser übergeben.
Stattdessen: Ein virtuelles DOM wird erstellt und mit dem vorherigen virtuellen DOM verglichen. Nur die Unterschiede werden zur Verarbeitung an den Browser übergeben.
Üblicherweise ist React sehr effizient dabei, Änderungen herauszufinden - doch es benötigt Hilfe, wenn Elemente in einem Array Wiederholt werden
Faustregel: Wenn wir in unserem JSX-Template .map
verwenden, sollten innere Elemente eine eindeutige key-Property haben, um React zu unterstützen
siehe auch: https://reactjs.org/docs/reconciliation.html
um Bundle-Größen von React-Apps zu reduzieren: Komponenten erst importieren, wenn sie benötigt werden
häufige Verwendung: importieren von einer Route erst, wenn sie aufgerufen wird
Imports in JavaScript:
import
als Statement: synchroner Import bevor der Rest der Datei ausgeführt wird (in webpack: automatisches Integrieren in das Bundle)import
als Funktion: asynchroner Import, wenn benötigtReact-Imports für das Lazy-Loading:
lazy
-FunktionSuspense
-Komponentemit React Router v5:
import { Suspense, lazy } from 'react';
import { Route } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
);
}
Quelle:
Route-based code splitting on reactjs.org