Come posso identificare più facilmente i colli di bottiglia nelle prestazioni di rendering di React?

Aug 15 2020

Sto riscontrando un problema con l'identificazione dei colli di bottiglia nelle prestazioni di rendering mentre lavoro su un visualizzatore JSON. Con pochi elementi si comporta bene, ma a un certo punto diventa fastidiosamente lento.

Controllando il profiler, sembra che il rendering degli elementi sia abbastanza veloce, ma ho notato alcuni problemi che non sono sicuro di come perseguire.

Panoramica

  • L'app è un visualizzatore JSON che consente di espandere/ridurre a icona tutti gli elementi contemporaneamente, nonché i singoli elementi.
  • Le prestazioni vanno bene con pochi elementi, ma sembrano diminuire drasticamente all'aumentare del numero di elementi.
  • Durante la profilazione sia del mio metodo di filtro degli oggetti performance.now()sia del controllo del tempo di rendering in React DevTools, le cifre sembrano a posto. Potrei interpretarlo male.
  • Ho provato React.memo()a utilizzare elementi senza stato (in particolare la chiave/valore che è il componente reso più frequentemente), ma non sembra migliorare notevolmente le prestazioni. Certo, non sono sicuro di aver compreso abbastanza il ragionamento alla base della memorizzazione dei componenti React per implementarlo in modo utile.

Implementazione

  • Attualmente, la mia app carica i dati in un genitore che alimenta un componente che carica l'albero JSON utilizzando un elemento ricorsivo.
  • Il caricamento del feed JSON dall'URL modifica lo stato del componente principale, che viene filtrato utilizzando un metodo helper che utilizza i valori immessi in un campo di input.

Questioni

Esistono due funzionalità che riproducono un tempo di risposta lento con documenti JSON (non così grandi):

  • Il pulsante Espandi tutto
  • I primi tasti premuti su una query di filtro

Con l'attuale implementazione, sia il filtraggio che l'espansione di tutto attivano una display: nonemodifica sugli elementi figlio e il comportamento mi porta a credere che sto facendo qualcosa di inefficiente per gestire questo caso d'uso.

Fasi di riproduzione

Il codice è disponibile qui:https://codesandbox.io/s/react-json-view-4z348

Con una build di produzione qui (non funziona meglio):https://csb-4z348.vercel.app/

Per riprodurre il problema, giocare con la funzione Espandi tutto (segno più accanto all'input del filtro) e alcuni input del filtro.

Quindi, prova a caricare un feed JSON con più elementi (puoi testare sul mio feed API GitHub ) e prova a filtrare/espandere tutto. Notare il maggiore successo in termini di prestazioni.

Quello che ho notato

  • Durante la registrazione di useEffect, la riduzione al minimo sembra causare ~ 2 volte il numero di rendering rispetto all'espansione di tutto.
  • Man mano che l'input del filtro diventa più specifico, le prestazioni (logicamente) migliorano con il rendering di meno elementi.

Domanda

Anche se apprezzerei una spintarella nella giusta direzione per questo caso specifico, ciò che mi incuriosisce di più è come identificare al meglio la causa di questi problemi di prestazioni.

Ho esaminato il windowing dell'output, ma non è la mia prima scelta e sono abbastanza sicuro che sto facendo qualcosa di sbagliato, piuttosto che la causa è il rendering di troppi elementi.

Apprezzo il tuo tempo e ti ringrazio in anticipo per tutti i suggerimenti che potresti fornire!

Risposte

2 HarrisonGrieve Aug 16 2020 at 15:50

Sembra che io abbia risposto alla mia stessa domanda. Il problema era un problema di riconciliazione dovuto all'utilizzo dell'UUID come supporto chiave nei miei componenti figlio, che ne causava il rendering ogni volta che lo stato di riduzione a icona cambiava. Dai documenti:

Le chiavi dovrebbero essere stabili, prevedibili e univoche. Le chiavi instabili (come quelle prodotte da Math.random()) causeranno la ricreazione non necessaria di molte istanze di componenti e nodi DOM, il che può causare un degrado delle prestazioni e lo stato perso nei componenti figlio.

Lascerò qui i passaggi per chiunque altro si imbatta in questo problema.


Dopo aver scavato (troppo a lungo) nel Performance Profiler, ho notato che ogni volta che minimizzavo o espandevo gli elementi, ogni bambino veniva montato di nuovo. Dopo aver consultato Google con una query più specifica, ho trovato questo post sul blog e mi sono reso conto che stavo commettendo questo flagrante errore di prestazioni.

Una volta trovata la fonte del problema, ho trovato molti altri riferimenti ad esso.

Dopo aver sistemato l'oggetto chiave, il tempo di interazione è diventato più veloce di circa il 60% per minimizzare/espandere tutto.

Infine, ho memorizzato alcuni altri componenti relativi al filtro istantaneo e finalmente sembra funzionare come vorrei per il momento.

Grazie a chiunque abbia dato un'occhiata a questo nel frattempo, e spero che sia utile per chiunque possa imbattersi in questo.