Metodi per migliorare e ottimizzare le prestazioni nelle app React
Pubblicato: 2022-03-10React consente alle applicazioni Web di aggiornare rapidamente le loro interfacce utente (UI), ma ciò non significa che la tua applicazione React di medie o grandi dimensioni funzionerà in modo efficiente. Le sue prestazioni dipenderanno da come utilizzi React durante la creazione e dalla tua comprensione di come opera React e del processo attraverso il quale i componenti vivono le varie fasi del loro ciclo di vita. React offre molti miglioramenti delle prestazioni a un'app Web e puoi ottenere questi miglioramenti attraverso varie tecniche, funzionalità e strumenti.
In questo tutorial, discuteremo vari metodi per ottimizzare le prestazioni nelle applicazioni React e anche le funzionalità di React che possiamo utilizzare per migliorare le prestazioni.
Da dove iniziare a ottimizzare le prestazioni in un'applicazione React?
Non possiamo iniziare a ottimizzare un'app senza sapere esattamente quando e dove ottimizzare. Potresti chiedere: "Da dove iniziamo?"
Durante il processo di rendering iniziale, React crea un albero DOM di componenti. Quindi, quando i dati cambiano nell'albero DOM, vogliamo che React esegua nuovamente il rendering solo di quei componenti che sono stati interessati dalla modifica, saltando gli altri componenti nell'albero che non sono stati interessati.
Tuttavia, React potrebbe finire per eseguire nuovamente il rendering di tutti i componenti nell'albero DOM, anche se non tutti sono interessati. Ciò comporterà tempi di caricamento più lunghi, spreco di tempo e persino risorse della CPU sprecate. Dobbiamo impedire che ciò accada. Quindi, è qui che concentreremo il nostro sforzo di ottimizzazione.
In questa situazione, potremmo configurare ogni componente in modo che renderizzi o differisca solo quando necessario, per evitare sprechi di risorse e tempo.
Misurare le prestazioni
Non avviare mai il processo di ottimizzazione della tua applicazione React in base a ciò che senti. Invece, utilizza gli strumenti di misurazione disponibili per analizzare le prestazioni della tua app React e ottenere un rapporto dettagliato di ciò che potrebbe rallentarla.
Analisi dei componenti React con la scheda Performance di Chrome
Secondo la documentazione di React, mentre sei ancora in modalità di sviluppo, puoi utilizzare la scheda "Prestazioni" nel browser Chrome per visualizzare come i componenti React vengono montati, aggiornati e smontati. Ad esempio, l'immagine seguente mostra la scheda "Prestazioni" di Chrome che profila e analizza il mio blog in modalità di sviluppo.
Per fare ciò, attenersi alla seguente procedura:
- Disabilita temporaneamente tutte le estensioni, in particolare React Developer Tools, perché possono alterare il risultato dell'analisi. Puoi disabilitare facilmente le estensioni eseguendo il browser in modalità di navigazione in incognito.
- Assicurati che l'applicazione sia in esecuzione in modalità di sviluppo. Cioè, l'applicazione dovrebbe essere in esecuzione sul tuo localhost.
- Apri gli Strumenti per sviluppatori di Chrome, fai clic sulla scheda "Prestazioni", quindi fai clic sul pulsante "Registra".
- Esegui le azioni che desideri profilare. Non registrare più di 20 secondi, altrimenti Chrome potrebbe bloccarsi.
- Interrompi la registrazione.
- Gli eventi React verranno raggruppati sotto l'etichetta "Temporizzazione utente".
I numeri del profiler sono relativi. La maggior parte delle volte e dei componenti verranno renderizzati più rapidamente in produzione. Tuttavia, questo dovrebbe aiutarti a capire quando l'interfaccia utente viene aggiornata per errore, nonché quanto in profondità e con quale frequenza si verificano gli aggiornamenti dell'interfaccia utente.
Profiler degli strumenti per sviluppatori Reagisci
Secondo la documentazione di React, in react-dom
16.5+ e react-native
0.57+, sono disponibili funzionalità di profilazione avanzate in modalità sviluppatore utilizzando React Developer Tools Profiler. Il profiler utilizza l'API Profiler sperimentale di React per raccogliere informazioni sui tempi di ciascun componente di cui viene eseguito il rendering, al fine di identificare i colli di bottiglia delle prestazioni in un'applicazione React.
Basta scaricare React Developer Tools per il tuo browser, quindi puoi utilizzare lo strumento profiler fornito con esso. Il profiler può essere utilizzato solo in modalità di sviluppo o nella build di profilazione della produzione di React v16.5+. L'immagine seguente è il riepilogo del profiler del mio blog in modalità di sviluppo utilizzando React Developer Tools Profiler:
Per ottenere ciò, attenersi alla seguente procedura:
- Scarica gli strumenti per sviluppatori React.
- Assicurati che la tua applicazione React sia in modalità di sviluppo o nella build di profilazione della produzione di React v16.5+.
- Apri la scheda "Strumenti per sviluppatori" di Chrome. Sarà disponibile una nuova scheda denominata "Profiler", fornita da React Developer Tools.
- Fare clic sul pulsante "Registra" ed eseguire le azioni che si desidera profilare. Idealmente, interrompi la registrazione dopo aver eseguito le azioni che desideri profilare.
- Apparirà un grafico (noto come flamegraph) con tutti i gestori di eventi e i componenti della tua app React.
Nota : per ulteriori informazioni, consultare la documentazione.
Memorizzazione con React.memo()
React v16 è stato rilasciato con un'API aggiuntiva, un componente di ordine superiore chiamato React.memo()
. Secondo la documentazione, questo esiste solo come ottimizzazione delle prestazioni .
Il suo nome, " memo ", deriva dalla memorizzazione, che è fondamentalmente una forma di ottimizzazione utilizzata principalmente per accelerare il codice memorizzando i risultati di chiamate di funzioni costose e restituendo il risultato memorizzato ogni volta che la stessa funzione costosa viene richiamata.
La memorizzazione è una tecnica per eseguire una funzione una volta, di solito una funzione pura, e quindi salvare il risultato in memoria. Se proviamo a eseguire nuovamente quella funzione, con gli stessi argomenti di prima , restituirà semplicemente il risultato salvato in precedenza dall'esecuzione della prima funzione, senza eseguire nuovamente la funzione.
Mappando la descrizione sopra all'ecosistema React, le funzioni menzionate sono componenti di React e gli argomenti sono oggetti di scena.
Il comportamento predefinito di un componente dichiarato utilizzando React.memo()
è che esegue il rendering solo se gli oggetti di scena nel componente sono cambiati. Esegue un confronto superficiale degli oggetti di scena per verificarlo, ma è disponibile un'opzione per ignorarlo.
React.memo()
migliora le prestazioni di un'app React evitando il re-rendering di componenti i cui prop non sono cambiati o quando il re-rendering non è necessario.
Il codice seguente è la sintassi di base di React.memo()
:
const MemoizedComponent = React.memo((props) => { // Component code goes in here })
Quando utilizzare React.memo()
- Pura componente funzionale
Puoi usareReact.memo()
se il tuo componente è funzionale, ha gli stessi prop e restituisce sempre lo stesso output. Puoi anche usareReact.memo()
su componenti non puri con hook React. - Il componente esegue il rendering spesso
Puoi usareReact.memo()
per avvolgere un componente che esegue il rendering spesso. - Il componente esegue nuovamente il rendering con gli stessi oggetti di scena
UsaReact.memo()
per avvolgere un componente che di solito viene fornito con gli stessi oggetti di scena durante il re-rendering. - Elementi medio alti
Usalo per un componente che contiene un numero medio-alto di elementi dell'interfaccia utente per verificare l'uguaglianza degli oggetti di scena.
Nota : fai attenzione quando memorizzi componenti che utilizzano oggetti di scena come callback. Assicurati di utilizzare la stessa istanza della funzione di callback tra i rendering. Ciò è dovuto al fatto che il componente padre potrebbe fornire istanze diverse della funzione di callback su ogni rendering, causando l'interruzione del processo di memorizzazione. Per risolvere questo problema, assicurati che il componente memorizzato riceva sempre la stessa istanza di callback.
Vediamo come possiamo usare la memorizzazione in una situazione del mondo reale. Il componente funzionale di seguito, chiamato "Foto", utilizza React.memo()
per impedire il re-rendering.
export function Photo({ title, views }) { return ( <div> <div>Photo title: {title}</div> <div>Location: {location}</div> </div> ); } // memoize the component export const MemoizedPhoto = React.memo(Photo);
Il codice sopra è costituito da un componente funzionale che visualizza un div contenente il titolo di una foto e la posizione del soggetto nella foto. Stiamo anche memorizzando il componente creando una nuova funzione e chiamandola MemoizedPhoto
. Memorizzare il componente foto impedirà il nuovo rendering del componente purché gli oggetti di scena, il title
e la location
siano gli stessi nei rendering successivi.
// On first render, React calls MemoizedPhoto function. <MemoizedPhoto title="Effiel Tower" location="Paris" /> // On next render, React does not call MemoizedPhoto function, // preventing rendering <MemoizedPhoto title="Effiel Tower" location="Paris" />
Qui, React chiama la funzione memorizzata solo una volta. Non eseguirà il rendering del componente nella chiamata successiva finché gli oggetti di scena rimangono gli stessi.
Raggruppamento e minimizzazione
Nelle applicazioni React a pagina singola, possiamo raggruppare e ridurre al minimo tutto il nostro codice JavaScript in un unico file. Questo va bene, purché la nostra applicazione sia relativamente piccola.
Man mano che la nostra applicazione React cresce, raggruppare e minimizzare tutto il nostro codice JavaScript in un unico file diventa problematico, difficile da capire e noioso. Influirà anche sulle prestazioni e sul tempo di caricamento della nostra app React perché stiamo inviando un file JavaScript di grandi dimensioni al browser. Quindi, abbiamo bisogno di un processo che ci aiuti a suddividere la base di codice in vari file e consegnarli al browser a intervalli secondo necessità.
In una situazione come questa, possiamo utilizzare una qualche forma di raggruppamento di risorse come Webpack e quindi sfruttare la sua funzionalità di suddivisione del codice per suddividere la nostra applicazione in più file.
La suddivisione del codice è suggerita nella documentazione di Webpack come mezzo per migliorare il tempo di caricamento di un'applicazione. È anche suggerito nella documentazione di React per il lazy-loading (che serve solo le cose attualmente necessarie all'utente), che può migliorare notevolmente le prestazioni.
Webpack suggerisce tre approcci generali alla suddivisione del codice:
- Punti di ingresso
Dividi manualmente il codice utilizzando la configurazione di immissione. - Prevenzione della duplicazione
UsaSplitChunksPlugin
per deduplicare e dividere i blocchi. - Importazioni dinamiche
Codice diviso tramite chiamate di funzioni in linea all'interno dei moduli.
Vantaggi della divisione del codice
- La suddivisione del codice aiuta con le risorse della cache del browser e con il codice che non cambia spesso.
- Aiuta anche il browser a scaricare le risorse in parallelo, riducendo il tempo di caricamento complessivo dell'applicazione.
- Ci consente di dividere il codice in blocchi che verranno caricati su richiesta o secondo necessità dall'applicazione.
- Mantiene relativamente piccolo il download iniziale delle risorse al primo rendering, riducendo così il tempo di caricamento dell'app.
Strutture di dati immutabili
La documentazione di React parla del potere di non mutare i dati. Tutti i dati che non possono essere modificati sono immutabili. L'immutabilità è un concetto che i programmatori React dovrebbero comprendere.
Non è possibile modificare un valore o un oggetto immutabile. Quindi, quando c'è un aggiornamento, viene creato un nuovo valore in memoria, lasciando inalterato quello vecchio.
Possiamo utilizzare strutture di dati immutabili e React.PureComponent
per verificare automaticamente la presenza di un cambiamento di stato complesso. Ad esempio, se lo stato nella tua applicazione è immutabile, puoi effettivamente salvare tutti gli oggetti di stato in un unico archivio con una libreria di gestione dello stato come Redux, che ti consente di implementare facilmente la funzionalità di annullamento e ripristino.
Non dimenticare che non possiamo modificare i dati immutabili una volta creati.
Vantaggi delle strutture di dati immutabili
- Non hanno effetti collaterali.
- Gli oggetti dati immutabili sono facili da creare, testare e utilizzare.
- Ci aiutano a scrivere una logica che può essere utilizzata per verificare rapidamente la presenza di aggiornamenti nello stato, senza dover controllare i dati più e più volte.
- Aiutano a prevenire l'accoppiamento temporale (un tipo di accoppiamento in cui il codice dipende dall'ordine di esecuzione).
Le seguenti librerie aiutano a fornire una serie di strutture di dati immutabili:
- aiutante di immutabilità
Muta una copia dei dati senza cambiare l'origine. - Immutabile.js
Le raccolte di dati persistenti immutabili per JavaScript aumentano l'efficienza e la semplicità. - senza soluzione di continuità immutabile
Le strutture di dati immutabili per JavaScript diventano retrocompatibili con i normali array e oggetti JavaScript. - Reagisci-copia-scrivi
Questo fornisce uno stato immutabile con un'API mutabile.
Altri metodi per migliorare le prestazioni
Utilizzare una build di produzione prima della distribuzione
La documentazione di React suggerisce di utilizzare la build di produzione ridotta durante la distribuzione dell'app.
Evita le funzioni anonime
Poiché alle funzioni anonime non viene assegnato un identificatore (tramite const/let/var
), non sono persistenti ogni volta che un componente viene inevitabilmente visualizzato di nuovo. Ciò fa sì che JavaScript allochi nuova memoria ogni volta che questo componente viene ridisegnato, invece di allocare un singolo pezzo di memoria solo una volta, come quando vengono utilizzate funzioni con nome.
import React from 'react'; // Don't do this. class Dont extends Component { render() { return ( <button onClick={() => console.log('Do not do this')}> Don't </button> ); } } // The better way class Do extends Component { handleClick = () => { console.log('This is OK'); } render() { return ( <button onClick={this.handleClick}> Do </button> ); } }
Il codice sopra mostra due modi diversi per fare in modo che un pulsante esegua un'azione al clic. Il primo blocco di codice utilizza una funzione anonima nel prop onClick()
e ciò influenzerebbe le prestazioni. Il secondo blocco di codice utilizza una funzione denominata nella funzione onClick()
, che è il modo corretto in questo scenario.
Il montaggio e lo smontaggio dei componenti è spesso costoso
L'uso di condizionali o tenary per far scomparire un componente (cioè per smontarlo) non è consigliabile, perché il componente fatto scomparire causerà il ridisegno e il reflow del browser. Questo è un processo costoso perché le posizioni e le geometrie degli elementi HTML nel documento dovranno essere ricalcolate. Invece, possiamo usare le proprietà di opacity
e visibility
dei CSS per nascondere il componente. In questo modo il componente sarà ancora nel DOM ma invisibile, senza alcun costo di performance.
Virtualizza liste lunghe
La documentazione suggerisce che se si esegue il rendering di un elenco con una grande quantità di dati, è necessario eseguire il rendering di una piccola parte dei dati nell'elenco alla volta all'interno della finestra visibile. Quindi, puoi eseguire il rendering di più dati mentre l'elenco viene fatto scorrere; quindi, i dati vengono visualizzati solo quando sono nella finestra. Questo processo è chiamato "finestra". In windowing, un piccolo sottoinsieme di righe viene visualizzato in un dato momento. Ci sono librerie popolari per fare questo, due delle quali sono gestite da Brian Vaughn:
- finestra di reazione
- reagire virtualizzato
Conclusione
Esistono molti altri metodi per migliorare le prestazioni della tua applicazione React. Questo articolo ha discusso i metodi più importanti ed efficaci per l'ottimizzazione delle prestazioni.
Spero che ti sia piaciuto leggere questo tutorial. Puoi saperne di più tramite le risorse elencate di seguito. Se hai domande, lasciale nella sezione commenti qui sotto. Sarò felice di rispondere a ognuno di loro.
Riferimenti e risorse correlate
- "Ottimizzazione delle prestazioni", React Docs
- "Usa React.memo saggiamente", Dmitri Pavlutin
- "Tecniche di ottimizzazione delle prestazioni in reazione", Niteesh Yadav
- "Immutabilità in reazione: non c'è niente di sbagliato negli oggetti mutanti", Esteban Herrera
- "10 modi per ottimizzare le prestazioni dell'app React", Chidume Nnamdi
- "5 suggerimenti per migliorare le prestazioni delle tue app React", William Le