Material-ui: Verbessern Sie die Leistung der Material-UI

Erstellt am 23. März 2018  ·  93Kommentare  ·  Quelle: mui-org/material-ui

Zunächst vielen Dank für diese tolle Komponentenbibliothek! Es ist großartig!

Ich habe in meiner neuen App eine Schublade hinzugefügt. Meistens habe ich ein Schubladenbeispiel kopiert. Nur für PoC habe ich multipliziert

        <Divider />
        <List>{mailFolderListItems}</List>

Sektion.

Danach fühlt es sich sehr langsam an, insbesondere auf mobilen Geräten (Nexus 4, Cordova mit Zebrastreifen 20). Ich benutze den Dev-Modus, aber der Prod-Modus beschleunigt nicht viel.

Durch React-Dev-Tools habe ich festgestellt, dass Komponenten in mailFolderListItems bei jedem Link-Klick im React-Router oder sogar im geöffneten Menü gerendert werden. Es dauert manchmal bis zu 50-60 ms, um ONE {mailFolderListItems} neu zu rendern. ich benutze

const modalProps = {
    keepMounted: true, // Better open performance on mobile.
};

Um Unsicherheiten mit anderen App-Komponenten auszuschließen, habe ich mailFolderListItems in Component umgewandelt und das erneute Rendern deaktiviert:

class MailFolderListItems extends React.Component<{}, {}> {

    shouldComponentUpdate() {
        return false;
    }

    render() {
        return (
            <List>
                <Link to={Routes.Startpage.path}>
                    <ListItem>
                        <ListItemIcon>
                            <InboxIcon />
                        </ListItemIcon>
[...]


                <Divider />
                <MailFolderListItems />

Danach fühlt sich dieser Teil OK an.

Ich habe https://github.com/mui-org/material-ui/issues/5628 gefunden. Ich schlage vor, es erneut zu besuchen . Die Optimierung von shouldComponentUpdate ist der grundlegende und wichtigste Schritt zur Leistungssteigerung. PureComponent ist nur die gebräuchlichste Verknüpfung.

Außerdem ist mir aufgefallen, dass sehr viel Zeit (1-2ms und mehr für JEDE material-ui-Komponente) in WithStyles aufgewendet wird.

  • [x] Ich habe die Ausgaben dieses Repositorys durchsucht und glaube, dass dies kein Duplikat ist.

Erwartetes Verhalten

Ich erwarte, die größtmögliche Reaktionsleistung für diese großartige Bibliothek zu erhalten.

Aktuelles Verhalten

Die App wird mit jeder material-ui-Komponente langsamer.

Schritte zur Reproduktion (für Fehler)

Ich stelle noch kein reproduzierendes Beispiel zur Verfügung, da ich nur von der Komponenten-Demoseite kopiert habe, aber bei Bedarf kann ich eine Codesandbox-Demo bereitstellen. Für Browser ist es auffällig, wenn der Browser in der Leistungseinstellung um Faktor >=5x verlangsamt wird.

Ihre Umgebung

| Technik | Version |
|-------------|---------|
| Material-UI | 1.0.0-beta.38 |
| Material-UI-Symbole | 1.0.0-beta.36 |
| Reagieren | 16.2.0 |
| Browser | Cordova Zebrastreifen 20 (entspricht Android Chrome 50) |

discussion performance

Hilfreichster Kommentar

@oliviertassinari als großartiger Entwickler, wie kann man keine Fakten? Ich denke, ich habe oben genug Fakten präsentiert. Ich habe alles getan, um es zu zeigen, weil ich dieses Projekt mag und einen Beitrag leisten möchte. Es ist nicht genug? Ok, dann mehr Fakten und _keine_ Annahmen.

Nur für dich reduziere ich auf 10 Buttons. 10! Es bedeutet, dass alle 10 Komponenten (schlimmer: Container) von material-ui die gesamte App verlangsamen, bis die App nicht mehr verwendbar ist! Getestet auf realem Gerät mit Zebrastreifen 21/Chrom 51 ohne jegliche Drossel:

Normale Taste:
image

PureButton:
image

Dies ist immer noch eine 8-fache Verbesserung! Es ist riesig! Können Sie sich vorstellen, wie wichtig dies auf mobilen Geräten ist?

Statt 100 Schaltflächen finden Sie auf einer Seite maximal 10 Schaltflächen. Trotzdem finden Sie 10 Raster, 10 X usw.

Ich habe Button verwendet, weil es eine der einfachsten Komponenten ist! Es zeigt, dass material-ui aus Leistungssicht gebrochen ist . Stellen Sie sich nun Containerkomponenten wie eine AppBar, Toolbar, Liste, Schublade vor! Das ist noch schlimmer! Auf jeder Seite gelangt man sehr schnell zu 20 Komponenten/Containern. Und da Sie auf Ihrem leistungsstarken Desktop/Mac keine Langsamkeit verlieren, heißt das nicht, dass material-ui nicht unglaublich langsam ist.

response-intl, wird die Prüfung zwischen den vorherigen und neuen Requisiten immer falsch sein. Sie werden CPU-Zyklen verschwenden. Also x13 -> x0.8

Zeigen Sie mir ein Beispiel auf Codesandbox. Ich sehe nicht, warum das passieren sollte. Ich bin mir sicher, dass dies nur bei falscher Verwendung von Reaktionskomponenten passiert. Und ein offizielles Beispiel zeigt eine falsche Verwendung, da es so aussieht, als ob response-intl nicht Context-Subscriber angewendet wurde. Aber es gibt viele andere Komponenten, die auf Reaction-Richtlinien und Best Practices abgestimmt sind, um leistungsfähig zu bleiben.

Übrigens: WithStyles verbrauchen bis zu 2,27 ms für den Button auf dem Mobilgerät. 8 Komponenten und du unter 60fps.

Alle 93 Kommentare

Ich erwarte, die größtmögliche Reaktionsleistung für diese großartige Bibliothek zu erhalten.

@Bessonov Performance wird ein Fokus nach Version 1 sein. Wir haben versucht, den Kern der Bibliothek so schnell wie möglich zu halten. Es sollte für +90% der Fälle gut genug sein. Zumindest ist es meine bisherige Erfahrung mit der Bibliothek. Sie haben keine externen Referenzen angegeben, abgesehen davon, dass wir darauf aufmerksam gemacht haben, dass Leistung wichtig ist, können wir dieses Problem nicht umsetzbar machen. Wenn Sie eine Leistungseinschränkung von Material-UI feststellen können, die wir untersuchen können (mit einer Reproduktionscodesandbox oder einem Repository), melden Sie diese bitte. Bis dahin schließe ich das Thema.

@oliviertassinari danke für deine schnelle Antwort! Ich freue mich sehr zu hören, dass diese Leistung nach der Veröffentlichung im Mittelpunkt stehen wird.

Es sollte für +90% der Fälle gut genug sein. Zumindest ist es meine bisherige Erfahrung mit der Bibliothek.

Auf dem Desktop - ja, das ist es. Aber auf dem Handy ist es wirklich langsam. Nur Schublade und einige Tasten machen die App verzögert. Es reagiert nicht und verbraucht bei Bedarf mehr Strom.

Sie haben keine externen Referenzen angegeben, abgesehen davon, dass wir darauf aufmerksam gemacht haben, dass Leistung wichtig ist, können wir dieses Problem nicht umsetzbar machen.

Ich habe einen Verweis auf bereits angesprochene Probleme und einen Verweis auf die Reaktionsdokumentation bereitgestellt.

Wenn Sie in der Lage sind, eine Leistungseinschränkung der Material-UI zu identifizieren, die wir untersuchen können (mit einer Reproduktionscodesandbox oder einem Repository)

Wie gesagt, ich kann es. Hier ist ein Vergleich von reiner und nicht reiner Komponente. Schritte zur Reproduktion sind:

  1. Verwenden Sie Chrome und gehen Sie zu https://codesandbox.io/s/r1ov818nwm
  2. Klicken Sie im Vorschaufenster auf "In einem neuen Fenster öffnen"
  3. Öffnen Sie die Entwicklungstools und gehen Sie zum Tab "Leistung"
  4. Drosseln Sie Ihre CPU mindestens 5x (je nach PC - kann 10x sein), um das mobile Gerät zu berücksichtigen
  5. Klicken Sie auf Burger und sehen Sie, wie es verzögert.

Und nun:

  1. Gehe zu index.js
  2. ändere pure in true
  3. Speichern
  4. Schritte von oben wiederholen
  5. Genieß es!

Dieses Beispiel ist etwas unrealistisch, da ich zu viele Listenelemente eingefügt habe. Aber wie gesagt, auf dem Handy kommt es sehr schnell zu einem Punkt, an dem man jede Aktion "fühlt".

Hier ist mein Problem mit WithStyles . Es ist auf dem Desktop mit CPU-Drosselung. Ich möchte am Montag Screenshots von einem echten Gerät posten.

Zeit für WithStyles(ListItem) :
image

Zeit für ListItem :
image

Der Unterschied beträgt 1,26 ms nur für eine ListItem Komponente. Mit 13 Komponenten wie dieser kann man nicht mehr mit 60fps rendern. Aber mir ist auf dem Desktop aufgefallen, dass diese Dauer nicht immer hier ist.

Hier ist ein Vergleich von reiner und nicht reiner Komponente

Übrigens, ich sage nicht, dass PureComponent DIE Lösung für alle Leistungsprobleme ist. Ich sage nur, dass es einer der Helfer sein kann, um material-ui mobil nutzbar zu machen.

Der Unterschied beträgt nur für eine ListItem-Komponente 1,26 ms.

@Bessonov Die WithStyles-Komponente sollte für sich wiederholende Instanzen derselben Komponente sehr günstig sein. Wir injizieren das CSS nur einmal.
Vielleicht erreichen Sie die React-Beschränkungen und die Kosten für Komponenten höherer Ordnung.
Es gibt eine Reihe von Lösungen, um Leistungsprobleme in React zu mildern. Sie können beispielsweise Element-Memoization und Virtualisierung verwenden. Ich halte dieses Problem als Ausgangspunkt für zukünftige Leistungsuntersuchungen offen.
Ich glaube nicht, dass wir jetzt viel dagegen tun können. Danke, dass Sie die Bedenken geäußert haben.

Okay, das ist nur ein Teil. Was denkst du über den wichtigeren Teil - Optimierung von shouldComponentUpdate ?

EDIT: Ich möchte dieses Thema in zwei Teile aufteilen. Dieser Teil ist ungefähr shouldComponentUpdate und der andere Teil für WithStyles , wenn ich mehr Informationen zeigen kann. Ist das okay für dich?

Optimieren von shouldComponentUpdate

@Bessonov Wir implementieren eine solche Logik aus zwei Gründen nicht absichtlich auf der Material-UI-Seite:

  1. Die meisten unserer Komponenten akzeptieren Reaktionselemente, eine Änderung der Reaktionselementreferenz bei jedem Rendern, wodurch die Logik systematisch CPU-Zyklen verschwendet.
  2. Je näher die shouldComponentUpdate Logik an der Wurzel des React-Baums liegt, desto effizienter ist sie. Ich meine, je mehr Sie den Baum beschneiden können, desto besser. Material-UI-Komponenten sind auf niedrigem Niveau. Es geringe Hebelwirkung Gelegenheit.

Die einzige Möglichkeit, die wir gefunden haben, sind die Symbole. Lassen Sie es uns wissen, wenn Sie neue identifizieren können.

der andere Teil für WithStyles

+90% der in WithStyles verbrachten Zeit wird tatsächlich in JSS verbracht, es gibt nur sehr wenige, die wir auf der Material-UI-Seite tun können.
Zumindest habe ich in letzter Zeit eine Caching-Möglichkeit für serverseitiges Rendering identifiziert, um die Leistung beim wiederholten Rendering erheblich zu verbessern. Aber es ist nur serverseitig.

Die meisten unserer Komponenten akzeptieren Reaktionselemente, eine Änderung der Reaktionselementreferenz bei jedem Rendern, wodurch die Logik systematisch CPU-Zyklen verschwendet.

Das ist nutzungsabhängig und kann verbessert werden. Ich vergleiche mich nicht selbst, aber ich bin sicher, dass die richtige Komponentenverwendung und optimierte shouldComponentUpdate (mit flachem Vergleich) weniger kostspielig sind als nicht optimierte Komponenten, insbesondere für unveränderliche Strukturen (und immutable.js ist nicht erforderlich). übrigens). Zugehöriges Ticket: https://github.com/mui-org/material-ui/issues/4305

In https://github.com/mui-org/material-ui/blob/v1-beta/docs/src/pages/demos/drawers/PermanentDrawer.js#L68 führt dies beispielsweise zu neuen Objekten und macht PureComponent sinnlos:

        classes={{
          paper: classes.drawerPaper,
        }}

weil es jedes Mal ein neues Objekt zurückgibt. Aber nicht:

const drawerClasses = {
    paper: classes.drawerPaper,
};
[...]
        classes={drawerClasses}

Gleiches für Komponenten.

Je näher die Logik von shouldComponentUpdate an der Wurzel des React-Baums liegt, desto effizienter ist sie. Ich meine, je mehr Sie den Baum beschneiden können, desto besser.

Ja das stimmt.

Material-UI-Komponenten sind auf niedrigem Niveau. Es geringe Hebelwirkung Gelegenheit.

Das stimmt teilweise. Wie Sie in https://codesandbox.io/s/r1ov818nwm sehen können, List in PureComponent . Nichts anderes und es beschleunigt Drawer um einen erheblichen Faktor. Ich würde behaupten, dass dies für jede Komponente gilt, zumindest die {this.props.children} . Ich habe eine weitere Demo erstellt: https://codesandbox.io/s/my7rmo2m4y

Es gibt nur 101 Buttons (pure = false) und Buttons, die in PureComponent verpackt sind (pure = true, siehe wo Button importiert wird). Gleiches Spielergebnis, wenn ich auf den "Click"-Button klicke:

Normale Taste:
image

Umwickelter Knopf (mit Overhead usw.):
image

Wie Sie sehen können, gibt es 637 ms vs. 47 ms für nur normale Buttons! Glauben Sie immer noch, dass es sich nicht lohnt, shouldComponentUpdate (oder nur PureComponent ) zu optimieren? :)

EDIT: Formulierung am Anfang korrigieren

@oliviertassinari @oreqizer Mir ist eine interessante Sache aufgefallen. Es sieht so aus, als ob die Erweiterung PureComponent besser abschneidet als Component mit

  shouldComponentUpdate() {
    return false;
  }

image

EDIT: Ich denke, dies ist eine der internen Optimierungsreaktionen.

Wie Sie sehen können, gibt es 637 ms vs. 47 ms für nur normale Buttons! Glauben Sie immer noch, dass sich optimieren sollteComponentUpdate (oder einfach nur PureComponent) nicht lohnen? :)

@Bessonov Es zeigt das Potenzial der reinen Logik. Ja, es kann sehr nützlich sein! Es ist eine x13-Verbesserung. Aber ich glaube nicht, dass es den realen Bedingungen nahe kommt:

  • Statt 100 Schaltflächen finden Sie auf einer Seite maximal 10 Schaltflächen. Trotzdem finden Sie 10 Raster, 10 X usw.
  • Anstelle von einfachen Eigenschaften, die den Elementen der unteren Ebene zur Verfügung gestellt werden, verwenden Sie etwas wie React-Intl, die Prüfung zwischen den vorherigen und neuen Requisiten wird immer falsch sein. Sie werden CPU-Zyklen verschwenden . Also x13 -> x0.8 oder so

@oliviertassinari als großartiger Entwickler, wie kann man keine Fakten? Ich denke, ich habe oben genug Fakten präsentiert. Ich habe alles getan, um es zu zeigen, weil ich dieses Projekt mag und einen Beitrag leisten möchte. Es ist nicht genug? Ok, dann mehr Fakten und _keine_ Annahmen.

Nur für dich reduziere ich auf 10 Buttons. 10! Es bedeutet, dass alle 10 Komponenten (schlimmer: Container) von material-ui die gesamte App verlangsamen, bis die App nicht mehr verwendbar ist! Getestet auf realem Gerät mit Zebrastreifen 21/Chrom 51 ohne jegliche Drossel:

Normale Taste:
image

PureButton:
image

Dies ist immer noch eine 8-fache Verbesserung! Es ist riesig! Können Sie sich vorstellen, wie wichtig dies auf mobilen Geräten ist?

Statt 100 Schaltflächen finden Sie auf einer Seite maximal 10 Schaltflächen. Trotzdem finden Sie 10 Raster, 10 X usw.

Ich habe Button verwendet, weil es eine der einfachsten Komponenten ist! Es zeigt, dass material-ui aus Leistungssicht gebrochen ist . Stellen Sie sich nun Containerkomponenten wie eine AppBar, Toolbar, Liste, Schublade vor! Das ist noch schlimmer! Auf jeder Seite gelangt man sehr schnell zu 20 Komponenten/Containern. Und da Sie auf Ihrem leistungsstarken Desktop/Mac keine Langsamkeit verlieren, heißt das nicht, dass material-ui nicht unglaublich langsam ist.

response-intl, wird die Prüfung zwischen den vorherigen und neuen Requisiten immer falsch sein. Sie werden CPU-Zyklen verschwenden. Also x13 -> x0.8

Zeigen Sie mir ein Beispiel auf Codesandbox. Ich sehe nicht, warum das passieren sollte. Ich bin mir sicher, dass dies nur bei falscher Verwendung von Reaktionskomponenten passiert. Und ein offizielles Beispiel zeigt eine falsche Verwendung, da es so aussieht, als ob response-intl nicht Context-Subscriber angewendet wurde. Aber es gibt viele andere Komponenten, die auf Reaction-Richtlinien und Best Practices abgestimmt sind, um leistungsfähig zu bleiben.

Übrigens: WithStyles verbrauchen bis zu 2,27 ms für den Button auf dem Mobilgerät. 8 Komponenten und du unter 60fps.

Warum verwenden Sie Ihre persönlichen Annahmen als Argumente und keine Fakten?

Was lässt Sie glauben, dass es eine persönliche Annahme ist? Ich habe versucht, kritisch zu denken. Konzeptionell wird die zusätzliche Prop-Traversierung die reine Version im Vergleich zur nicht reinen Version verlangsamen, sie muss etwas beschneiden, um es wert zu sein. Gleiche Argumentation wie #5628 oder https://github.com/react-bootstrap/react-bootstrap/issues/633#issuecomment -234749417 oder https://github.com/reactstrap/reactstrap/pull/771#issuecomment -375765577

Mit rein:
capture d ecran 2018-03-27 a 11 43 41

Ohne rein:
capture d ecran 2018-03-27 a 11 44 15

Die Reproduktion ist die folgende.

@oliviertassinari bist du dir sicher, dass codeandbox alles richtig macht für deinen Test? Denn meine Ergebnisse sind ganz anders:

Ohne pure (auch ohne Gas ist es langsam):
image

Pure (nach Änderung auf true und speichern bekomme ich eine neue URL für Codesandbox):
image

Da es keine Kontextänderungen überprüft und die Benutzer auch dazu zwingt, sicherzustellen, dass alle untergeordneten Komponenten auch "rein" sind, glaube ich nicht, dass dies für diese Bibliothek geeignet ist. Diese Bibliothek muss so flexibel wie möglich sein, und ich glaube, dies wird die Nutzung erschweren.

Ich sehe den Punkt. Aber es ist ein sehr interessanter Kompromiss. Auf der einen Seite auch Material-ui sollte „so flexibel wie möglich“, aber auf der anderen Seite die aktuelle Leistung

Vielleicht sollten Sie darüber nachdenken, reine und nicht-reine Versionen von Komponenten bereitzustellen. Ich mache es jetzt für meine App, um auch auf dem Desktop eine brauchbare Leistung zu erzielen.

@Bessonov Die Codesandbox wurde nicht richtig gespeichert. Es tut uns leid. Es fehlte das folgende Diff. Ich habe den Link aktualisiert:

<Button>
+ <i>
    Button
+ </i>
</Button>

Ich verstehe nicht, warum es zu unterschiedlichen Ergebnissen kommen sollte? Ich bekomme ein besseres Diagramm, aber die nicht reine Version ist deutlich langsamer.

EDIT: ok, ich verstehe. Versuchen Sie herauszufinden, was los ist...

Okay, ich verstehe jetzt. Genau das gleiche "neues Objekt bei jedem Render"-Ding. Mir ist es vorher nicht aufgefallen. In einigen Fällen kann es mit Konstanten durch das Babel-Plugin automatisch verbessert werden.

Na, dann weißt du es schon! :D Auch wenn es für sich genommen nicht viel Nutzen bringt (Sie haben ~7% gezeigt), ist es mit reinen Komponenten immer noch rentabel, um einige der oben genannten Fehler zu vermeiden. Ich habe es jetzt mit Pure Wrapper + Babel-Plugin + Produktions-Build getestet und es ist beeindruckend schnell auf demselben Mobilgerät!

Wie gesagt, ich denke, es ist am besten, beides anzubieten - nicht reine Komponenten für Flexibilität und reine Komponenten (Wrapper reichen aus, um es einfach und wartungsfreundlich zu halten) für die Leistung. Aber für mich kann ich nur mit reinen Komponenten leben, da die allgemeine Leistungssteigerung viel größer ist als die Leistungsnachteile. Oder besser: Ich kann material-ui nicht ohne reine Komponenten verwenden.

Ok, jetzt warte ich auf weiteren Input zu diesem Thema und erstelle eigene Wrapper in meiner App )

Danke für Einblicke!

Ich habe noch nie davon gehört, dass Transform-React-Constant-Elemente tatsächlich verwendet werden und wirklich nützlich sind. Es ist in Ordnung, eine zufällige Mikrooptimierung hinzuzufügen, aber in der Praxis schreibt man selten Code, der einfach genug ist, um viel daraus zu machen. Obwohl ich vermute, dass es keine schlechte Optimierung für alle Symbolkomponenten im SVG-Stil wie <Add /> .

Schauen Sie sich dieses Beispiel an (klicken Sie auf Plugins in der Seite, suchen Sie nach "react-constant" und klicken Sie auf die Checkbox auf "transform-react-constant-elements") und Sie werden sehen, dass kaum etwas optimiert wurde:

  • Das einfache statische InputAdornment wurde nach oben verschoben, juhu.
  • Aber der trivial einfache Senden-Button wurde nicht mehr optimiert, sobald wir einen Event-Handler darauf setzten.
  • Und während das InputAdornment selbst jetzt hochgezogen wird, ist das InputProps={{startAdornment: ...}} immer noch inline und erzeugt bei jedem Rendern ein neues Objekt, was PureComponent unmöglich macht.
  • Ebenso macht es classes={{label: classes.runButtonLabel}} für PureComponent unmöglich, die Schaltfläche Ausführen zu optimieren.

Ich persönlich mag PureComponent und versuche es absolut überall einzusetzen und alles so gut es geht zu optimieren, damit es funktioniert. Aber es sieht nicht so aus, als wäre MUI so gemacht, dass PureComponent funktionieren würde.

  • Die *Props Requisiten wie InputProps sind ein grundlegendes Muster für die Funktionsweise von MUI. Es ist nicht nur eine erweiterte Möglichkeit, MUI-Interna bei Bedarf zu ändern, sondern wird in einfachen Anwendungsfällen regelmäßig verwendet. Dieses Muster macht oft jede Blattkomponente, die normalerweise im reinen Modus optimiert werden könnte, nicht optimierbar.
  • Ebenso funktioniert das Muster classes={{...}} auch nicht mit PureComponent und es ist die Möglichkeit, den Dingen in MUI beliebige Stile hinzuzufügen. (Und zu sagen, classes={classes} ist in keiner Weise praktisch, weil ein echter Verbraucher wahrscheinlich einen anderen Klassennamen hat als die interne Klasse der Komponente und classes wahrscheinlich auch Klassen einschließt, die andere Elemente stylen sollen in der gleichen verbrauchenden Komponente)
  • Kinder werden viel verwendet, statt 1 Komponente verwendet ein gemeinsames Muster oft 2-3 und nur eine davon kann rein optimiert werden, wenn eine von ihnen kann.

Wenn wir etwas optimieren wollen, müssen diese grundlegenden Probleme behandelt werden, sonst wird es nicht wirklich viel optimieren, wenn man Leute ein reines MUI aktivieren lässt. Ich kann mir zwei Möglichkeiten vorstellen.

Wir lassen nur zu, dass der reine Modus aktiviert wird, Verbraucher müssen Objekte auswendig lernen oder manuell heben, um tatsächlich Optimierungen zu erhalten

  1. Eine Möglichkeit, die mir einfällt, wäre etwas weniger als shallowMemoize das das lokale this und ein key (damit Sie es für verschiedene Datenbits verwenden können). der die Daten speichert, solange sie flach gleich sind
  2. Eine andere, die ich mir vorstellen kann, ist die Verwendung von Reselect, um Selektoren zu erstellen, die Daten speichern.
  3. Eine andere Möglichkeit, das shallowMemoize in 1. auszuführen, wäre, es mit einem Dekorateur an render() zu übergeben. Auf diese Weise könnten wir bei jedem Rendern ein neues übergeben und anstatt key benötigen, können wir einfach prüfen, ob eines der memoisierten Objekte aus dem letzten Rendern wiederverwendet werden soll, und dann alle alten Werte verwerfen.

Das Problem ist natürlich, dass dies die Verbraucher viel größer und unübersichtlicher macht und erfordert, dass die Logik manuell an Stellen hochgezogen wird, an denen sie weit vom Code entfernt ist, der sie verwendet.

import {createSelector} from 'reselect';

class FormPage extends PureComponent {
  state = { example: '' };

  change = e => this.setState({example: e.target.value});
  submit = () => {
    console.log('Submit: ', this.state.example);
  };

  runButtonClasses = createSelector(
    props => props.classes.runButtonLabel,
    runButtonLabel => ({runButtonLabel}));

  render() {
    const {title} = this.props;
    const {example} = this.state;

    return (
      <form>
        {title}
        <TextField
          InputProps={this.shallowMemoize('a', {
            // This example assumes use of transform-react-constant-elements to make this object always the same
            startAdornment: <InputAdornment position="start">Kg</InputAdornment>,
          }}}
          onChange={example}
          value={example} />
        <Button classes={this.runButtonClasses(classes)}>Run</Button>
        <Button onClick={this.submit}>Submit</Button>
      </form>
    );
  }
}
// ...
  <strong i="6">@withShallowMemoize</strong>
  render(memo) {
    const {title} = this.props;
    const {example} = this.state;

    return (
      <form>
        {title}
        <TextField
          InputProps={memo({
            startAdornment: <InputAdornment position="start">Kg</InputAdornment>,
          }}}
          onChange={example}
          value={example} />
        <Button classes={memo(classes)}>Run</Button>
        <Button onClick={this.submit}>Submit</Button>
      </form>
    );
  }

Anstatt zu versuchen, InputProps, Klassen usw. zu optimieren, ermutigen wir die Leute, Mikrokomponenten für alle ihre Anwendungsfälle zu entwickeln

Wenn dies die empfohlene Methode zur Verwendung von MUI ist, benötigen wir möglicherweise nicht einmal einen reinen Modus. Wie Sie sehen können, können diese Komponenten, sobald Sie mit der Erstellung kleiner Hilfskomponenten für Ihre üblichen Anwendungsfälle beginnen, leicht reine Komponenten sein. Im Beispiel wird WeightTextField jetzt nie neu gerendert, solange value immer noch gleich ist, wobei Styles, jegliche Memoisierungsarbeit für InputProps oder das InputAdornment-Setup komplett übersprungen wird. Und wenn sich value ändert, müssen wir TextField sowieso neu rendern, damit das nicht reine InputProps={{...}} keine Rolle spielt.

Mit diesem Weg komme ich gut zurecht. Ich mag Mikrokomponenten theoretisch; obwohl ich jede derzeit gültige Syntax/jedes gültige Muster zum Schreiben hasse, das mir einfällt. Ich möchte nicht MyComponent = enhance(MyComponent) , ich möchte sie dekorieren, aber Sie können keine der kurzen Möglichkeiten zum Schreiben einer winzigen Komponente dekorieren. Ich mag es auch nicht, aus import {TextField} from 'material-ui'; import WeightTextField from '../../../ui/WeightTextField ;` zu machen.

```js
let WeightTextField = ({unit, InputProps, ...props}) => (
{...Requisiten}
InputProps={{
startSchmuck:{Einheit} ,
...EingabeProps
}}
onChange={Beispiel}
Wert={Beispiel} />
);
WeightTextField = pure(WeightTextField);

RunButton = Compose(
withStyles(theme => ({
Etikett: {
Schriftgewicht: '800',
},
})),
rein
)(Taste);

const SubmitButton = pure(Button);

Klasse FormPage erweitert Component {
Zustand = { Beispiel: '' };

change = e => this.setState({Beispiel: e.target.value});
einreichen = () => {
console.log('Submit: ', this.state.example);
};

render() {
const {title} = this.props;
const {Beispiel} = this.state;

return (
  <form>
    {title}
    <WeightTextField
      unit='Kg'
      onChange={example}
      value={example} />
    <RunButton>Run</RunButton>
    <SubmitButton onClick={this.submit}>Submit</SubmitButton>
  </form>
);

}
}
````

Ich habe einen Anwendungsfall, bei dem ich 500–2000 Kontrollkästchen auf einer Seite in einer großen Liste anzeigen muss. Bei Verwendung nativer Browser-Checkboxen ist die Leistung in Ordnung, aber bei Verwendung der <Checkbox> Komponente ist die Leistung sehr schlecht und skaliert linear mit der Anzahl der Checkboxen auf der Seite. Beispiel: https://codesandbox.io/s/5x596w5lwn

Ich verwende mui@next – gibt es eine Strategie, die ich _jetzt_ anwenden kann, um dies praktikabel zu machen?

@wilsonjackson
Führen Sie zunächst nicht Folgendes aus. Dadurch wird bei jedem Kontrollkästchen bei jedem Rendern ein neuer Handler erstellt, wodurch alle PureComponent-Optimierungen, die Sie versuchen, deaktiviert werden.

  handleChange = index => event => {
    this.setState({

Zweitens, erstellen Sie Ihre eigene kleine Komponente, um Checkbox zu umschließen und diese Komponente rein zu machen. Dies hat den zusätzlichen Vorteil, dass Sie alle Eigenschaften hinzufügen können, die allen Ihren Kontrollkästchen gemeinsam sind. Und da Sie das häufige Problem haben, für jedes Element einen anderen Änderungsereignishandler zu benötigen, können wir eine Klassenkomponente verwenden und dies in der Komponente anstelle des Listencontainers tun.

https://codesandbox.io/s/r7l64j6v5n

Wenn wir etwas optimieren wollen, müssen diese grundlegenden Probleme behandelt werden, sonst wird es nicht wirklich viel optimieren, wenn man Leute ein reines MUI aktivieren lässt. Ich kann mir zwei Möglichkeiten vorstellen.

@dantman Diese API-Entscheidungen wurden getroffen, um den DX so weit wie möglich zu verbessern und gleichzeitig schnell genug zu sein.

Anstatt zu versuchen, InputProps, Klassen usw. zu optimieren, ermutigen wir die Leute, Mikrokomponenten für alle ihre Anwendungsfälle zu entwickeln

Ja, machen wir. Das Wrapping-Muster ist definitiv die empfohlene Möglichkeit, die Bibliothek anzupassen. Es kann erweitert werden, um Leistungsoptimierungen anzuwenden. Im Userland ist es einfacher, wo die Variabilität der Nutzung der Komponenten viel geringer ist. Wir könnten sogar eine FAQ-Frage oder einen Leitfaden zu diesem Punkt in die Dokumentation einfügen.

Ja, machen wir. Das Wrapping-Muster ist definitiv die empfohlene Möglichkeit, die Bibliothek anzupassen. Es kann erweitert werden, um Leistungsoptimierungen anzuwenden. Im Userland ist es einfacher, wo die Variabilität der Nutzung der Komponenten viel geringer ist. Wir könnten sogar eine FAQ-Frage oder einen Leitfaden zu diesem Punkt in die Dokumentation einfügen.

Okay. In diesem Fall:

  • Wir möchten Bibliotheken wie recompose empfehlen, die das Schreiben kleiner zustandsloser Komponenten erleichtern. Verdammt, ich würde es begrüßen, wenn mir jemand sagen würde, dass es ein anderes Muster oder eine andere Bibliothek gibt, die recompose ähnlich ist, aber das Erstellen reiner zustandsloser Funktionen wie die Muster von recompose ermöglicht, aber so schön ist wie die Verwendung der Decorator-Syntax
  • Wie wir Vorschläge zur Verwendung der verschiedenen Autocomplete-Bibliotheken und CSS in js-Bibliotheken mit MUI auflisten, möchten wir wahrscheinlich auch die verschiedenen vorhandenen Ordnerorganisationsmuster und Pfad-Hacking-Bibliotheken vorschlagen (die, mit denen Sie Importe von ../../../../ui/Foo aktivieren können) something-local/Foo ), das verwendet werden kann, um die Verwendung Ihrer eigenen lokalen Reihe von Mikrokomponenten, die MUI umhüllen, so angenehm wie die Verwendung von import {TextField} from 'material-ui'; ist und sich nicht wie eine Regression in der Entwicklerfreundlichkeit anfühlt .

@dantman fantastisch, danke.

Ich musste sCU mehrmals anwenden, da withStyles (oder besser JSS) sehr langsam ist. Ich kenne den Code von JSS nicht, aber es fühlt sich an, als könnte er ziemlich viel optimiert werden. Normalerweise verwende ich gestylte Komponenten oder glamourös und lande so bei JSS und einem der anderen in der App und beide übertreffen JSS.

Diese Fälle mögen zwar etwas ärgerlich sein, sind aber mit sCU auf App-Ebene oder intelligenteren Statusupdates leicht zu umgehen. Ich habe noch nicht gesehen, dass eine einzelne MUI-Komponente langsam genug ist, um problematisch zu sein, und ich muss auch noch wirklich in MUI codieren, um viel Zeit in Anspruch zu nehmen.

Um nicht zu sagen, dass es nicht schneller sein könnte und sicher wäre es schöner, wenn weniger Aufsicht erforderlich wäre, aber zumindest nach meiner Ansicht wäre es in diesem Fall besser, JSS direkt als MUI zu optimieren.

@Pajn Danke für das Feedback. Es wäre wirklich toll, eine Situation zu sehen, in der die Leistung von withStyles problematisch ist oder in der sie von gestylten Komponenten übertroffen wird.

Hat jemand dieses Repo https://github.com/reactopt/reactopt überprüft?

$ click - button (text: مقالات ) => CssBaseline,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,ScrollbarSize,TransitionGroup,TouchRipple,Ripple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,Main,ScrollbarSize,TransitionGroup,TouchRipple,Ripple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple,TransitionGroup,TouchRipple

Diese Komponenten machen unnötige Neu-Renderings nur für einen einfachen Klick, warum versuchen Sie es nicht einfach mit dem Lebenszyklus der Komponentenaktualisierung ?

@nimaa77

Hat jemand dieses Repo https://github.com/reactopt/reactopt überprüft?
Nein, ich verwende einfache Warum-Haben-Sie-Update- und Chrome/React-Dev-Tools-Funktionen.

Diese Komponenten machen unnötige Neu-Renderings nur für einen einfachen Klick, warum versuchen Sie es nicht einfach mit dem Lebenszyklus der Komponentenaktualisierung ?

Siehe Diskussion oben. Das passt nicht für jeden Anwendungsfall. Für mich scheint es, als ob material-ui beides bieten sollte - reine und nicht reine Versionen von Komponenten. Ich sehe eine enorme Leistungssteigerung mit reinen Komponenten.

@dantman

Schauen Sie sich dieses Beispiel an (klicken Sie auf Plugins in der Seite, suchen Sie nach "react-constant" und klicken Sie auf die Checkbox auf "transform-react-constant-elements") und Sie werden sehen, dass kaum etwas optimiert wurde:

Dies ist nur eine Möglichkeit, dieses Problem anzugehen. Es gibt auch andere Optionen, Plugins und andere handgemachte Optimierungen. Versteh mich nicht falsch, aber ich bin nicht an theoretischen Diskussionen interessiert, welche Optimierungen gut oder schlecht sind. Ich interessiere mich für praktische Optimierung, die FUNKTIONIERT. Zumindest in meinem Setup. Alles, was ich oben geschrieben habe, FUNKTIONIERT für mich und macht meine App zumindest auf Low-Mid-Mobilgeräten nutzbar. Obwohl ich gezwungen bin, mehr Optimierungen wie das Neuordnen von Komponentenbäumen vorzunehmen, wäre die erreichte Leistung ohne reine Komponenten und andere automatische Optimierungen nicht möglich. Und ja, ich profiliere und optimiere sehr viel, um dies zu erreichen.

@Bessonov vielleicht können wir eine Requisite verwenden, um die Methode shouldComponentUpdate für einen flachen Vergleich zu verwenden (https://reactjs.org/docs/shallow-compare.html) ODER immer false zurückzugeben,
dies erhöht nicht die Bundlegröße mit zwei Versionen von Komponenten (rein und normal)

@lucasljj Ich erwarte keine signifikante Zunahme der Bundle-Größe, wenn dies wie oben erwähnt als Wrapper für zustandsorientierte Komponenten erfolgt. Siehe auch Profile oben: Nur reine Komponenten sind schneller als return false; in sCU.

Das Problem mit reinen Komponenten oder Komponenten, die sCU implementieren, ist, wenn Sie darin nicht reine Komponenten oder children . Siehe Aussagen oben. Ein weiteres Problem, das angegangen werden muss, ist der Themenwechsel. Nicht getestet, aber ich denke, dies kann zumindest mit der Context-API überwunden werden.

@bossonov Pure Components gelten als die Standardreaktion, die nicht darauf zurückzuführen ist, dass sie zu spät zur Party gekommen sind. Aber ich stimme zu, dass die meisten Leute Redux-Formular-Wrapper erstellen werden, um das Fehlen im Unterbaum zu mildern

Das Problem, auf das Sie sich bezüglich reiner Komponenten und untergeordneter Elemente beziehen, tritt nur auf, wenn die Requisiten der obersten Ebene nicht auf die untergeordneten Elemente übertragen werden. Jede Eigenschafts- oder Zustandsänderung löst ein erneutes Rendern durch den untergeordneten Baum bis zu der Ebene aus, auf der sich die Eigenschaft nicht ausbreitet.
Denn die Idee von reinen Komponenten ist, dass sie bei jeder Prop- oder Zustandsänderung neu rendert. Ich kenne die Interna nicht genau, aber ich habe mich gefragt, ob Sie keine komponentenspezifische Änderungseigenschaft verwenden können, die Sie an jedes Kind weitergeben. Sie können zu diesem Zweck sogar die neue Kontext-API verwenden, um nicht eine Requisite durch den gesamten Baum weitergeben zu müssen.

@oliviertassinari

Leistung wird ein Schwerpunkt nach Version 1 sein.

Großartig, v1 ist veröffentlicht :) Gibt es eine Idee, wann die Leistung angegangen werden sollte? Mir ist aufgefallen, dass dieses Problem nicht Teil des Post v1-Meilensteins ist.

@Bessonov Ich denke, es wäre großartig, etwas Zeit damit zu verbringen, die ROADMAP zu aktualisieren. Bezüglich der Leistung. Ich habe zwei Dinge im Sinn, die umsetzbar sind, aber ich hoffe, mehr zu entdecken:

  1. Wir können nichts verbessern, was wir nicht sehen. Wir brauchen einen Maßstab. Ich habe bisher zwei interessante Bibliotheken gesehen:
  2. Das Rendern der CSS-Serverseite erfordert ~30% dessen, was React zum Rendern benötigt. Weil wir style = f(props) nicht unterstützen (nun, wir unterstützen es noch nicht: #7633). Wir können eine sehr effiziente Caching-Funktion implementieren, die die Kosten für wiederholte Anfragen um fast 0% senkt. Daran könnte ich für das Büro arbeiten, wenn die SSR-Leistung unsere Geschäftskennzahlen beeinträchtigt.
    Aber es sind nicht die einzigen Optionen, wir können uns auch Babel-Plugins vorstellen, die die kostspieligen JSS-Voreinstellungen zur Kompilierungszeit statt zur Laufzeit anwenden (um die Auswirkungen auf die Bundle-Größe auszugleichen) cc @kof.

Danke für die URLs.

Stimme 1 voll und ganz zu und verlinkte Nr. 4305 oben.

  1. SSR kann beim Laden der ersten Seite helfen, hilft jedoch nicht bei langsamem Rendern oder Cordova. Während ich persönlich das erstmalige Laden auf dem Desktop oder sogar auf dem Handy ignorieren kann, wirkt sich das langsame erneute Rendern immer noch auf mobile Geräte nach dem ersten Laden aus.

Kompilierungszeitoptimierungen wären großartig und sollten aus meiner Sicht bevorzugt werden, da sie das erstmalige Laden und erneute Rendern verbessern können.

Eine andere Sache ist eine Art Dokumentation oder ein Beispielprojekt, wie man Mui (und Babel usw.) verwendet, um eine bessere Leistung zu erzielen.

Ich weiß nicht, ob jss-cache verwendet werden kann.

Die ISTF + Vorverarbeitungspipeline kann einige Millisekunden einsparen.

Am Sa, 19. Mai 2018, 19:06 schrieb Anton Bessonov [email protected] :

Danke für die URLs.

Stimme 1 voll und ganz zu und verlinkte #4305
https://github.com/mui-org/material-ui/issues/4305 oben.

  1. SSR kann beim Laden der ersten Seite helfen, aber nicht bei langsamen
    Rendern oder Cordova. Während ich persönlich das erstmalige Laden ignorieren kann
    Desktop oder sogar mobil, das langsame Rendern wirkt sich nach wie vor auf mobile Geräte aus
    erste Beladung.

Kompilierzeitoptimierungen wären toll und sollten aus meiner Sicht auch
bevorzugt werden, da es das erstmalige Laden und erneute Rendern verbessern kann.

Eine andere Sache ist eine Art Dokumentation oder ein Beispielprojekt zur Verwendung von mui
(und babel etc.), um eine bessere Leistung zu erzielen.

Ich weiß nicht, ob jss-cache http://cssinjs.org/jss-cache?v=v3.0.0 sein kann
Gebraucht.


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/mui-org/material-ui/issues/10778#issuecomment-390418709 ,
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/AADOWGrCxNGqrT4MijiX8r9Ad32z6RsJks5t0FEtgaJpZM4S4woq
.

Kann beim Einrichten von Benchmarks hilfreich sein:
https://github.com/A-gambit/CSS-IN-JS-Benchmarks/blob/master/RESULT.md

Oh React-jss (welches von material-ui verwendet wird) scheint ziemlich langsam zu sein

@janhoeck ist es nicht, und Sie können das

@kof Ich verstehe ziemlich gut, dass wirklich langsam. Tatsächlich ist die Gesamtleistung von material-ui nicht gerade glänzend.

4x-6x im Vergleich zu Aphrodite ist wirklich langsam

@Bessonov Die Benchmark- Methodik ist der Schlüssel . Sie können es beispielsweise in Ihrem Browser versuchen mit:
https://twitter.com/necolas/status/954024318308007937?lang=fr

Die Ergebnisse in meinem Browser:
⚠️ mit der Ungewissheit

capture d ecran 2018-06-12 a 17 58 54

Die Gesamtleistung von Material-UI ist nicht glänzend

Wir könnten sehr wohl durch React selbst und die Kosten der Komponenten eingeschränkt sein.

@Bessonov Was auch immer Sie verwendet haben, um die Leistung zu überprüfen, wenn es um Aphrodite geht, ist dies nicht der Fall, da es so schnell wie möglich verwendet und das Rendering verzögert, sodass die meisten Benchmarks die CPU-Zeit messen, aber nicht die endgültige Renderingleistung. Davon abgesehen ist die von

In Bezug auf die MUI-Leistung können Sie schneller werden, wenn Sie überhaupt keine Abstraktionen verwenden, aber das ist nicht der Punkt. MUI als nächstes ist ziemlich schnell. Es ist so schnell, dass Sie sich keine Sorgen um die Leistung machen sollten, es sei denn, SIE haben etwas völlig falsch gemacht oder die Bibliothek missbraucht.

@kof nein, MUI ist standardmäßig nicht schnell. Wir sind gezwungen, Problemumgehungen zu implementieren, um eine akzeptable Leistung zu erzielen. Nur ganz sicher: hast du nicht gesehen, dass es um mobile Geräte geht und nicht um High-End-Macs?

Machen Sie sich keine Sorgen um die Leistung, es sei denn, SIE haben etwas völlig falsch gemacht oder die Bibliothek missbraucht

Nun, oben sind einige Codesanbox-Beispiele. Wenn wir nur MUI-Komponenten in einem Container verwenden und Eingabewerte im Container-Zustand speichern, wie es in Komponenten-Demos gezeigt wurde, wird es sehr schnell unbrauchbar. Packen Sie PureComponent ein, verwenden Sie unkontrollierte Komponenten, ordnen Sie den Baum neu an, konstante Elemente, merken Sie sich und so weiter, um eine akzeptable Leistung aufrechtzuerhalten. Sie können gerne zeigen, wie Sie MUI richtig verwenden, um bessere Ergebnisse zu erzielen, zum Beispiel in (atm wir verwenden keine Schublade mehr):
https://codesandbox.io/s/r1ov818nwm

@oliviertassinari danke für den Link. Hier sind die Ergebnisse für Chrome 66 auf dem Nexus 4. Wie Sie sehen, sind die Ergebnisse 10x schlechter. Ich denke, in Crosswalk 21/Chrome 51 kann es etwas langsamer sein:

screenshot_2018-06-12-18-20-19

Wir könnten sehr wohl durch React selbst und die Kosten der Komponenten eingeschränkt sein.

IMHO nicht unbedingt, wie von anderen auch erwähnt. Jedes vermiedene Rendern ist ein großer Gewinn für Leistung und Akku. Vergessen Sie nicht, Leistung ist der dritte Punkt auf Ihrer Roadmap :+1:

Sehen Sie es so: Wenn das, was Sie mit cssinjs auf einem mobilen Gerät tun, zu langsam ist, sollten Sie diese Reaktion und die meisten anderen Dinge auch nicht verwenden. Wenn cssinjs Sie verlangsamt, verlangsamt Sie die Reaktionskomponente viel mehr. Sie müssen die Abstraktion der Ebene ob überdenken oder Optimierungen anwenden.

@oliviertassinari ist das evtl. ist es möglich, dass der mui-Wrapper die jss-Stile beim Update neu rendert? Möglicherweise gibt es ein Leck, das unnötige Arbeit verrichtet.

@kof

Sehe es so [...]

Ich verstehe dein Argument. Reagieren und reagieren (reine) Komponenten sind jedoch kein Flaschenhals für sich und funktionieren sehr gut. Zum Beispiel verwendet MUI keine PureComponent (mit den oben beschriebenen Vor- und Nachteilen), aber sie sind unser Leben sicherer. @oliviertassinari erwähnte, dass es eine Chance gibt, eine bessere Leistung zu erzielen, indem Caches oder Pre-Compiler verwendet werden.

Versteh mich nicht falsch, ihr seid tolle Jungs und ich freue mich wirklich über jede MUI-Version :clap: Aber man muss auch die Leistung berücksichtigen, denn nicht jeder Benutzer besucht Websites über einen Hochleistungs-Desktop .

Wenn Reagieren kein Leistungsengpass ist und Sie sich dessen sicher sind, wird JSS es auch nicht sein. Das einzige, was ich mir vorstellen kann, das validiert werden muss, wenn unnötige Arbeit im Gange ist und CSS beim Update neu generiert wird.

Eine reine Komponente ist etwas, das SIE in Ihrem Code auf Anwendungsebene verwenden sollten, wenn Sie eine Optimierung benötigen. MUI muss dies nicht tun, es ist eine generische Bibliothek und sollte keine Annahmen treffen. PureComponent ist nicht immer gut.

JSS hat die Leistung vom ersten Tag an berücksichtigt und ich habe endlose Stunden mit Mikrooptimierungen verbracht.

Das ist lächerlich. Ich kann nicht verhindern, dass materielle UI-Komponenten bei jeder einzelnen Interaktion neu gerendert werden. Egal, was ich tue, einmal so etwas Einfaches:

class RowText extends Component {
  shouldComponentUpdate = (nextProps, nextState) => {
    return false;
  };

  render() {
    const { title, artist } = this.props;
    return <ListItemText primary={title} secondary={artist} />;
  }
}

Löst erneute Renderings der zugrunde liegenden Typografieelemente aus. Wie ist das überhaupt möglich?

Hey, das passiert nur mit Liste und Listenelement. Wieso den ? Gibt es etwas, was ich tun kann?

@danielo515 Ohne das vollständige Beispiel schwer zu sagen. Die List Komponenten verwenden auch den Kontext. Wenn Sie also auch diesen Wert ändern, werden auch erneute Renderings ausgelöst.

Ich verstehe das @eps1lon. Könnten Sie jedoch bitte erklären, wie es zu einer Änderung des Kontexts kommen kann? Ich übergebe nichts an die Listenkomponente, sondern gebe nur einen Verlust von Kindern darin wieder.

@danielo515 Grundsätzlich jedes Mal, wenn List gerendert wird. Die neue Kontext-API ermöglichte einige Optimierungsstrategien, die wir untersuchen konnten, indem wir den Kontextwert auswendig lernten.

Derzeit reagiert die Kontext-API bei jedem Consumer, wenn der Wert WRT auf strikte Gleichheit ändert. Da wir bei jedem Render-Aufruf von List ein neues Objekt erstellen, wird auch jeder Consumer neu rendern.

Im Moment wäre ein einfacher Perf-Boost also, die List umschließen, damit sie nicht so häufig neu gerendert werden. Ich würde jedoch empfehlen, zuerst Benchmarks durchzuführen oder das Rendern früher abzubrechen. Wiederholte Renderaufrufe Typography sollten nicht so schlimm sein.

Das Umschließen von Komponenten mit React.memo funktioniert gut für alles ohne Kinder. Ich habe ein Formular mit 3 ExpansionPanel und 14 FormControl und es verzögert sich auf dem Desktop.

Ohne eine Lösung für diese Leistungsprobleme kann ich material-ui nicht weiter verwenden: s

@prevostc Ohne ein Beispiel ist es schwer zu sagen, was los ist.

@prevostc ohne eine Codesandbox-Reproduktion zu sehen, können weder wir noch Sie wissen, ob dies überhaupt mit diesem Problem zusammenhängt

@prevostc Erstellen Sie Ihre eigenen Komponenten, die keine Kinder benötigen, die Sie dann

Dh Erstellen Sie Ihre eigene reine/mit Notizen versehene MyExpansionPanel Komponente, die Daten-/Ereignis-Requisiten, aber keine Kinder aufnimmt und für das Rendern eines einzelnen Erweiterungspanels verantwortlich ist. Verwenden Sie dann <MyExpansionPanel ... /> , um jedes Ihrer Erweiterungspanels zu rendern. Dann werden die erneuten Renderings auf ein einzelnes Erweiterungspanel (oder zwei beim Übergang zwischen zwei) beschränkt.

@oliviertassinari @kof @dantman
Hier ist eine Codesandbox-Reproduktion meines Leistungsproblems: https://codesandbox.io/s/yvv2y2zxxx

Es handelt sich um ein Formular mit ~20 Feldern (nicht ungewöhnlich). Bei der Benutzereingabe treten Verzögerungen auf. Auf langsameren Geräten ist dieses Formular einfach nicht verwendbar.

Das Leistungsproblem kommt von der massiven Neurenderung bei Benutzereingaben, aber das Einpacken von MUI-Komponenten in eine reine Komponente (React.memo) bewirkt nichts, da hier alles Kinder und Kinder hat. reactjs.org/docs/react-api.html#reactpurecomponent)

Unten sind einige Screenshots meines manuellen Benchmarks, einer ohne Optimierung, einer mit allem auswendig gelernt und einer mit einer benutzerdefinierten Eingabekomponente mit einem lokalen Status, um zu vermeiden, dass der Status zu oft auf das gesamte Formular gesetzt wird.
Bei jeder Konfiguration dauert ein Abgleich der Benutzereingabe etwa 60 ms (weit mehr als die 16 ms, die ich zum Rendern mit 60 fps benötige.

Bitte beachte, dass ich gerne erfahren würde, dass ich etwas falsch gemacht habe, weil ich MUI liebe und ich traurig wäre, wenn es keine einfache Lösung gäbe <3

@dantman Wie kann man ein ExpansionPanel schreiben, das keine React.ReactNode als Eingabe benötigt (Kinder oder Requisiten)? Wenn Sie meinen, dass ich für jedes Panel in meiner App eine bestimmte Komponente schreiben soll, ist dies leider nicht möglich, ich habe zu viele davon.

screenshot 2018-12-20 at 22 04 08

screenshot 2018-12-20 at 21 56 57

screenshot 2018-12-20 at 22 05 00

@dantman Wie kann man ein ExpansionPanel schreiben, das keine React.ReactNode als Eingabe benötigt (Kinder oder Requisiten)? Wenn Sie meinen, dass ich für jedes Panel in meiner App eine bestimmte Komponente schreiben soll, ist dies leider nicht möglich, ich habe zu viele davon.

Ja, anstatt eine einzelne massive Komponente mit einem tief verschachtelten Baum von Platten zu erstellen, trennen Sie die Erweiterungsplattenteile in Komponenten. Es ist unmöglich, massive Bäume so zu optimieren.

Es ist nicht unmöglich. React-Komponenten sind sehr leicht. Kopieren Sie einen Codeblock aus einer massiven Komponente und fügen Sie ihn in eine Funktion ein, und Sie sind fast fertig, danach müssen Sie nur noch die Requisiten anschließen. Und solange Sie React.memo funktionieren und vermeiden, dass Dinge passieren, die die reine Optimierung der Requisiten stören, werden die Dinge ziemlich einfach optimiert.

Denken Sie daran, wenn Sie eine kleine Komponente erstellen, um ein Stück aus einer großen Komponente herauszubrechen. Es muss nichts Komplexes mit eigener Datei und eigener Prop-Validierung sein. Es kann eine einfache funktionale Komponente in derselben Datei wie die Komponente sein, in der sie verwendet wird, ohne propTypes. Dh Sie können kleine Komponenten erstellen, die nur für die Komponente in derselben Datei wie sie verfügbar sind.

Ja, die Material-Benutzeroberfläche ist etwas langsamer als dom-Elemente auf niedriger Ebene. Aber selbst wenn ein MUI Input 10x langsamer wäre als ein rohes input und die Dinge nach 100 zu langsam wurden. Dann hast du auch ohne MUI immer noch ein Problem, denn selbst wenn es 10x schneller ist, ist das rohe input würde die Site genauso langsam machen, wenn Sie 1000 davon hätten. Sie können in React keine einzelnen monolithischen Komponenten verwenden, selbst wenn Sie keine MUI verwenden. Sie müssen Ihre App in vernünftig große Blöcke aufteilen, damit React definierte Grenzen hat, die optimiert werden können.

@prevostc Hier ist Ihre Demo optimiert, indem Sie das Erweiterungsfeld in eine winzige Komponente mit derselben Datei

Ich möchte anmerken, dass dies nicht nur ein gutes Optimierungsmuster ist, sondern ein gutes Codierungsmuster. In echtem Code, wo Sie kein Array von Eingaben vornehmen, sondern diese explizit mit definierten Namen, Labels und Zwecken ausschreiben. Das Finden von Grenzen und sich wiederholenden Mustern optimiert nicht nur die Dinge, sondern reduziert auch die Boilerplate, wodurch Code trockener und lesbarer wird. dh anstelle von InputWrapper würde ich diese FormControl+InputLabel+FormHelperText+Input-Kombination wahrscheinlich in eine kleine lokale SimpleTextInput-Komponente aufteilen. Was es nicht nur optimieren würde (was dazu führt, dass nicht zusammenhängende Eingaben nicht erneut gerendert werden), sondern auch, dass der Code den zusätzlichen Boilerplate nicht wiederholen muss.

https://codesandbox.io/s/0o7vw76wzp

screen shot 2018-12-20 at 2 51 31 pm

Als ich dies las, kam ich zu dem Schluss, dass Sie zur Optimierung von Mui kleinere spezifische Komponenten erstellen müssen. Das habe ich bereits erkannt und mit Erfolg ausprobiert. Ich habe jedoch auch verstanden, dass es keine Möglichkeit gibt, Listenkomponenten zu optimieren, da die Kontext-API alle Eingabeeigenschaften ändert.

Grüße

Ok, hier ist ein aktualisierter Stresstest https://codesandbox.io/s/wz7yy1kvqk

Ich stimme @dantman in diesem allgemeinen Punkt zu https://github.com/mui-org/material-ui/issues/10778#issuecomment -449153635 ABER ich hatte bei dieser geringen Anzahl von Komponenten kein solches Leistungsproblem erwartet, aber tragen Sie es mit Ich habe die Quelle meines Leistungsproblems gefunden.

Ist JSS langsam? (Spoiler-Alarm: nein)

In Bezug auf einige der früheren Kommentare in diesem Thread habe ich im Stresstest ein Kontrollkästchen hinzugefügt, um alle Aufrufe von withStyles zu entfernen, und bin zu dem Schluss gekommen, dass JSS schnell ist und nicht die Ursache des Perf-Problems ist (wie @kof in https://github.com/mui-org/material-ui/issues/10778#issuecomment-396609276 darauf hingewiesen hat).

screenshot 2018-12-22 at 15 17 26

Die Reparatur

Für meinen speziellen Anwendungsfall konnte ich das Problem lokalisieren, dass jede Formulareingabe bei der Formularaktualisierung neu gerendert wurde, obwohl sich nur eine Eingabe tatsächlich geändert hat.
Im Screenshot unten habe ich sowohl das FormControl als auch die Eingabe in eine memoisierte Komponente verpackt, um ein Rendern zu vermeiden, wenn sich der Wert nicht ändert. @dantman schlug tatsächlich vor, dass ich für jedes ExpansionPanel eine bestimmte Komponente erstelle, aber dies ist eine viel weniger generische Lösung. Nebenbei bemerkt, jedes Panel wird immer noch neu gerendert und die Leistung ist alles andere als optimal, aber für den Moment reicht es aus.

screenshot 2018-12-22 at 15 18 22

So? Was kommt als nächstes?

Ich denke, dass es keine Möglichkeit gibt, diese Art von Problemen mit einer Änderung des Material-UI-Codes zu vermeiden, ohne eine massive Änderung der aktuellen API vorzunehmen, die sich stark auf die Zusammensetzung von React.ReactNode stützt.
Aber wie @dantman in https://github.com/mui-org/material-ui/issues/10778#issuecomment -449153635 erwähnt hat, ist MUI etwas langsamer als erwartet. Das überhaupt nicht anzusprechen ist IMHO ein Fehler.

Da wir uns dieses Problems bewusst sind, müssen wir möglicherweise eine Dokumentseite zu Leistungsproblemen und deren Behebung erstellen. Auch wenn seine Seite hauptsächlich auf das offizielle Dokument (https://reactjs.org/docs/optimizing-performance.html) umleitet und Komponenten auflistet, die Leistungsprobleme verursachen könnten, ist dies ein Anfang.
Es wäre besser, den Benutzer zu console.warn, wenn ein solches Problem auftritt, aber ich kann keine Möglichkeit finden, Probleme auf der Ebene der Material-UI zu erkennen.

@prevostc Diese Nachricht hat mir den Tag

ich nicht :so

Ich kenne die interne MUI nicht genug, um eine Idee zu haben, wie man die Rohleistung (ohne API-Änderung) im Moment verbessern kann. Ich arbeite an einigen Ideen, aber bis heute nichts schlüssiges: Ich habe eine App, bei der eine Radiogruppe erneut rendert, wenn sie nicht direkter Elternteil ist, kann dies noch nicht lokal reproduzieren.

Jede API-Änderung würde in Betracht ziehen, alle React.ReactNode Requisiten aus der API zu entfernen (Kinder und andere Requisiten wie Symbol usw.), aber ich konnte keine gute Möglichkeit finden, die gleiche Konfigurierbarkeit beizubehalten.
Folgendes habe ich versucht: https://codesandbox.io/s/jpw36jw65 (Warnung: nicht fertig).

Außerdem ist zu beachten, dass MUI im Entwicklungsmodus besonders langsam ist, da die Reaktion im Entwicklungsmodus besonders langsam ist. Ich weiß nicht, ob es eine Möglichkeit gibt, das zu verbessern.

Gibt es Fortschritte beim Hinzufügen von Funktionen (wie die von @Bessonov vorgeschlagenen), um die Leistungsprobleme zu optimieren, mit denen Material-UI derzeit konfrontiert ist?
Als wir anfingen, diese großartige Bibliothek für unser Projekt zu verwenden, wusste ich nicht, dass solche Leistungsprobleme auftreten können, wenn das Projekt immer größer wird. Außerdem habe ich in den Material-UI-Dokumenten keinen Abschnitt gesehen, der mich über Fälle informiert, die dazu führen können, dass Material-UI langsam wird und UX schadet.
In dieser Ausgabe werden viele Performance-Probleme gemeldet, die direkt oder indirekt mit der Material-UI zusammenhängen. Ich denke, es ist eine gute Idee, sie in einer anderen Ausgabe aufzulisten, damit jeder den Fortschritt verfolgen kann. Wenn Sie denken, dass es in Ordnung ist, kann ich eine neue Ausgabe eröffnen.

@mkermani144 Wir haben noch keinen Leistungsbericht gesehen, der direkt mit etwas korreliert, das Material-UI falsch macht. Bisher wurde dieses Thema als Hilfeforum für Menschen mit Problemen verwendet. Bestätigen Sie, dass nichts klagbares gemeldet wurde? Ich werde das Offensichtliche feststellen ,

Tisch

Entwicklermodus

Nehmen wir das Beispiel einer Tabelle. Es ist eine Komponente, die die Leute langsam finden. Wir haben die Virtualisierung dokumentiert , sie hilft sehr.

Im folgenden Testfall rendern wir 100 Items im Dev- Modus. Wir können folgende Fälle berücksichtigen:

  1. Table Raw: https://codesandbox.io/s/v066y5q7z3 : 63ms im Rendering
  2. Tabellenmaterial-UI-Master: https://codesandbox.io/s/m4kwmvj9ly : 250ms im Render
  3. Tabellenmaterial-UI Weiter: https://codesandbox.io/s/2o35yny1jn : 262ms im Render

Der Aufwand für die Verwendung von Material-UI im Entwicklungsmodus über das Hostelement beträgt also etwa x4 (der Unterschied ist in der Produktion geringer!), einfach weil wir Zwischenkomponenten erstellen. Aus diesem Grund wird die Virtualisierung nach dem Rendern einer Liste von ~100 Tabellenelementen wichtig. Jetzt können wir ein wenig in das Warum eintauchen

  1. Tabelle Raw + Funktionskomponente. Warum eine Funktionskomponente? Wir wollen die verwendeten Klassennamen abstrahieren. Wir möchten nicht, dass die Komponentenbenutzer sie wiederholen.
    https://codesandbox.io/s/1zl75mwlpj : 105ms im Rendering
  2. Tabelle Raw + Funktionskomponente + forwardRef. Warum forwardRef? Wir möchten, dass die Komponente "transparent" ist und mit einer ref auf das Host-Element zugreifen kann.
    https://codesandbox.io/s/32o2y0o9op : 120ms im Rendering
  3. Table Raw + Funktionskomponente + forwardRef + withStyles. Warum mitStyles? Weil wir unser Bauteil stylen wollen:
    https://codesandbox.io/s/j2n6pv768y : 200ms im Rendering
  4. Table Raw + Funktionskomponente + forwardRef + makeStyles. makeStyles ist möglicherweise schneller als withStyles, versuchen wir es:
    https://codesandbox.io/s/yw52n07l3z : 130ms im Rendering.

Wir haben also einen Hebel zur Verfügung: Migrieren Sie alle Komponenten von withStyles zu makeStyles. (- 70) 262 / (262) in dem Entwicklermodus Wir können über + 30% der Leistung gewinnen.

Produktionsmodus

Ich habe die gleichen Testfälle im Produktionsmodus ausgeführt:

  • n°1 30ms im Hydrat
  • n°3 106ms im Hydrat
  • n°4 40ms im Hydrat
  • n°5 41ms im Hydrat
  • n°6 80ms im Hydrat
  • n°7 57ms im Hydrat

Die Migration von withStyles zu makeStyles ist also im Produktionsmodus theoretisch immer noch eine Beschleunigung von +30%.

Wenn Sie denken, dass es in Ordnung ist, kann ich eine neue Ausgabe eröffnen.

@mkermani144 Wenn Sie einen bestimmten Fall haben, in dem Material-UI es falsch macht, sicher.

Ich habe alle Kommentare unter dieser Ausgabe gelesen. Mein Problem passt in keines der anderen oben genannten.

Ich habe eine List Komponente, die einige ListItem s enthält, von denen eine ausgewählt und hervorgehoben ist. Wenn ich auf ein anderes ListItem klicke, um es auszuwählen und hervorzuheben, wird die gesamte Liste (mit ihren Kindern) erneut gerendert.

Ich weiß, dass dieses Problem möglicherweise genauso aussieht wie ein vorheriger Kommentar , aber es ist nicht so. Zumindest glaube ich das.

Schauen wir uns die Ergebnisse von React Profiler an:

image
Wie Sie sehen können, habe ich eine MyList Komponente auf der obersten Ebene des Bildes. Diese Komponente ist nur ein Wrapper um MUI List und macht sie einfach rein, das heißt:

class MyList extends React.PureComponent {
  render() {
    return (
      <List>
        {this.props.children}
      </List>
    );
  }
}

Ich habe diese Wrapper weil @ eps1lon in einem seiner Kommentare erwähnt , dass Re-Rendering List verursacht Kontext zu aktualisieren und diese Kontextaktualisierung macht alle Verbraucher (einschließlich ListItem s) zur Wieder machen zu .

Ich habe auch versucht, alle meine ListItem s rein zu machen und die App erneut zu profilieren. Die Ergebnisse waren die gleichen, außer dass meine benutzerdefinierte Komponente (dh MyListItem ) nicht _sich_ neu gerendert hat, sondern alle darunter liegenden Kinder __did__.

Ich weiß, dass das Problem auftritt, weil der Kontext, den MUI für das Styling verwendet, _irgendwie_ neu rendert. Aber ich weiß nicht, warum dieses erneute Rendern passiert und wie ich es vermeiden kann.

Oder mache ich etwas falsch?

Hinweis: Ich verwende die neue (Alpha-)Styling-Lösung von MUI, dh @material-ui/styles . Ich weiß nicht, ob das wichtig ist.

@mkermani144 Entfernen Sie Material-UI mit nativen Elementen, beachten Sie, dass das erneute Rendering noch vorhanden ist. Die reine Logik wird so nicht helfen. React.createElement erstellt bei jedem Rendern neue Referenzen, es macht Ihre PureComponent ungültig.

Ja, ich weiß, dass Elemente Objekte sind und Objekte in Javascript nicht genau gleich sind, also schlägt sCU fehl.

Aber ich verstehe nicht, was Sie meinen, wenn Sie sagen, dass React.createElement wieder aufgerufen wird. Welcher Anruf an createElement ? Wenn Sie die Aufrufe innerhalb von List meinen, ruft es nur createElement für seine Kinder ( ListItem s) auf, nur wenn es erneut gerendert wird. Wenn es nicht erneut gerendert wird, wird kein createElement aufgerufen und es sollte nicht erneut gerendert werden. Das Problem ist, dass List selbst neu gerendert wird.

@mkermani144 Wenn Sie ein minimales Reproduktionsbeispiel erstellen können, können wir es uns ansehen.

Ihr MyList (und somit List ) wird neu gerendert, weil die Komponente, die MyList rendert (nennen wir es MyComponent ) neu gerendert wird. PureComponent auf MyList hilft nicht, da MyComponent neu gerendert und neue untergeordnete Elemente für MyList sodass die Prüfung von MyList fehlschlägt.

Ihr MyComponent wahrscheinlich neu gerendert, weil Sie dort den Status des ausgewählten Elements speichern.

Ich denke, die MUI-Implementierung von List sollte geändert werden, um den List-Kontextwert nicht bei jedem Rendering neu zu erstellen
hier: https://github.com/mui-org/material-ui/blob/fb4889f42613b05220c49f8fc361451066989328/packages/material-ui/src/List/List.js#L57

Lassen Sie List stattdessen etwa so aussehen:

const List = React.forwardRef(function List(props, ref) {
  const {
    children,
    classes,
    className,
    component: Component,
    dense,
    disablePadding,
    subheader,
    ...other
  } = props;
  const context = React.useMemo(() => ({ dense }), [dense]);

  return (
    <Component
      className={clsx(
        classes.root,
        {
          [classes.dense]: dense && !disablePadding,
          [classes.padding]: !disablePadding,
          [classes.subheader]: subheader,
        },
        className,
      )}
      ref={ref}
      {...other}
    >
      <ListContext.Provider value={context}>
        {subheader}
        {children}
      </ListContext.Provider>
    </Component>
  );
});

Das würde das Erstellen von sCU Versionen der ListItems vereinfachen

Ihre MyList (und damit Liste) wird neu gerendert, weil die Komponente, die MyList rendert (nennen wir sie MyComponent), neu gerendert wird. PureComponent auf MyList hilft nicht, da MyComponent neu gerendert und neue untergeordnete Elemente für MyList erstellt wurden, sodass die MyLists-Prüfung fehlschlägt.

@Pajn Nein, sieh dir meine React-Profiler-Ergebnisse an . MyList wurde nicht neu gerendert (es ist grau), aber List tat es (es ist blau). Ich beharre nicht auf PureComponent für MyList . Obwohl ich sCU für MyList implementiere, damit es nicht neu gerendert wird, wird List __neu gerendert__.

@oliviertassinari
Ich habe ein minimales Reproduktionsbeispiel erstellt:

import React, { Component } from 'react';

import StylesProvider from '@material-ui/styles/StylesProvider';
import ThemeProvider from '@material-ui/styles/ThemeProvider';

import { createMuiTheme } from '@material-ui/core';

import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';

const theme = createMuiTheme({});

const MyListItem = React.memo(ListItem, (prev, next) => prev.selected === next.selected);

class App extends Component {
  state = {
    selected: null,
  }
  render() {
    return (
      <StylesProvider>
        <ThemeProvider theme={theme}>
          <List>
            {[0, 1, 2, 3, 4].map(el => (
              <MyListItem
                button
                selected={el === this.state.selected}
                onClick={() => this.setState({ selected: el })}
              >
                {el}
              </MyListItem>
            ))}
          </List>
        </ThemeProvider>
      </StylesProvider>
    );
  }
}

export default App;

Reagieren Sie Profiler-Ergebnisse (nach Klick auf den 4. Listenpunkt):

image

Wie Sie sehen, funktioniert es wie erwartet, dh es gibt keine zusätzlichen Re-Renderings (außer für ButtonBase Komponenten innerhalb von ListItem s). Das Problem ist, dass diese Reproduktion _zu minimal_ ist; Es gibt viele Dinge, die ich darin überspringe.

Ich weiß, dass Sie mir nicht sagen können, was mit meinem Code nicht stimmt, was zu zusätzlichen Re-Renderings führt. Aber ich stelle Ihnen eine Frage: Was kann ein erneutes Rendern in den WithStylesInner Komponenten verursachen, die MUI-Komponenten umschließen?

@mkermani144 Was halten Sie von diesem Fix?

--- a/packages/material-ui/src/List/List.js
+++ b/packages/material-ui/src/List/List.js
@@ -40,6 +40,13 @@ const List = React.forwardRef(function List(props, ref) {
     ...other
   } = props;

+  const context = React.useMemo(
+    () => ({
+      dense,
+    }),
+    [dense],
+  );
+
   return (
     <Component
       className={clsx(
@@ -54,7 +61,7 @@ const List = React.forwardRef(function List(props, ref) {
       ref={ref}
       {...other}
     >
-      <ListContext.Provider value={{ dense }}>
+      <ListContext.Provider value={context}>
         {subheader}
         {children}
       </ListContext.Provider>

Möchten Sie einen Pull-Request senden? :) Wir verwenden dieselbe Strategie mit der Table-Komponente. Es funktioniert super. Vielen Dank für die Meldung des Problems!

@oliviertassinari Klar. Dies ist genau das, was @Pajn zuvor vorgeschlagen hat. Ich habe eine PR eingereicht.

14934 wird zusammengeführt; die Komponente List kann jedoch zu einem Leistungsengpass werden, wenn sie groß genug ist, egal wie sehr sie optimiert ist. Sollten wir nicht ein Beispiel bereitstellen, das die Verwendung von react-window oder react-virtualized mit der List Komponente zeigt, wie wir sie in den Table Dokumenten haben?

Sollten wir nicht ein Beispiel bereitstellen, das die Verwendung von React-Window oder React-Virtualized mit der List-Komponente zeigt, wie wir es in den Tabellendokumenten haben?

Das wäre toll :+1:

Fwiw Ich habe eine Chat-App erstellt und die App muss eine große Liste von Kontakten rendern. Ich bin auf das gleiche Problem gestoßen
@mkermani144 hatte.

https://stackoverflow.com/questions/55969987/why-do-the-children-nodes-rerender-when-the-parent-node-is-not-even-being-update/55971559

@henrylearn2rock Haben Sie überlegt, Virtualisierung zu verwenden? Wir haben eine Demo für die Liste hinzugefügt: https://next.material-ui.com/demos/lists/#virtualized -list.

Das hat mich auch echt gestolpert. Ich denke, die meisten Leute (einschließlich mir) gingen davon aus, dass alles unter einer reinen Komponente vor einem erneuten Rendern sicher ist, was bei dieser Bibliothek offensichtlich nicht der Fall ist. Ich werde es mit Virtualisierung versuchen, wie Sie zuletzt vorgeschlagen haben. Vielen Dank!

Ich denke, die meisten Leute (einschließlich mir) gingen davon aus, dass alles unter einer reinen Komponente vor einem erneuten Rendern sicher ist, was bei dieser Bibliothek offensichtlich nicht der Fall ist.

So funktioniert React.PureComponent oder React.memo nicht. Es betrifft nur die Komponente selbst. Kinder müssen möglicherweise immer noch neu rendern, wenn sich der Kontext ändert.

@pytyl Können Sie den Code teilen, in dem Sie eine PureComponent verwendet haben und erwartet haben, dass er ein erneutes Rendern in seinem Unterbaum verhindert?

@eps1lon Die folgende Dokumentation lässt es so aussehen, als würde die Rückgabe von false von shouldComponentUpdate automatisch die erneute Darstellung in untergeordneten Komponenten überspringen.
https://reactjs.org/docs/optimizing-performance.html#shouldcomponentupdate -in-action

Da shouldComponentUpdate für den Unterbaum, der auf C2 verwurzelt ist, false zurückgab, hat React nicht versucht, C2 zu rendern, und musste daher shouldComponentUpdate auf C4 und C5 aufrufen.

Vielleicht irre ich mich da? Das Folgende ist eine Momentaufnahme meines Profilers. Nur zum Testen gebe ich explizit false für shouldComponentUpdate in meiner Menükomponente zurück:

Screen Shot 2019-05-08 at 7 46 32 PM

Dadurch wurden alle meine untergeordneten Komponenten (Kategorien, Kategorie, Kategorieelemente, Kategorieelemente) nicht erneut gerendert. Viele MUI-bezogene Dinge scheinen unten neu gerendert zu werden, was eine große Verzögerung zu verursachen scheint. Sachen wie withStyles, Typography, ButtonBase. React ist noch ein bisschen neu, also entschuldigt bitte meine Unwissenheit. Unten ist mein Code für die Menükomponente (wobei ich für shouldComponentUpdate false zurückgebe):

import React, { Component } from "react";
import Categories from "./Categories";
import { withStyles, Paper } from "@material-ui/core";

const styles = theme => ({
  root: {
    paddingTop: 0,
    marginLeft: theme.spacing.unit * 2,
    marginRight: theme.spacing.unit * 2,
    marginTop: theme.spacing.unit * 1
  }
});

class Menu extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    if (nextProps.categories.length == this.props.categories.length) {
      return false;
    }
    return true;
  }

  render() {
    const { classes, categories } = this.props;

    return (
      <Paper className={classes.root}>
        <Categories categories={categories} />
      </Paper>
    );
  }
}

export default withStyles(styles)(Menu);

Ich bräuchte eine vollständige Codesandbox, um das Problem zu verstehen.

@eps1lon Ich werde versuchen, einen für morgen zu produzieren. Dankeschön.

@eps1lon hier ist der Codepen:
https://codesandbox.io/s/348kwwymj5

Kurze Beschreibung
Es ist eine einfache Menü-App für Restaurants (die oft mehr als 100 Menüpunkte haben). Wenn der Benutzer auf einen Menüpunkt klickt, öffnet er einen "Zur Bestellung hinzufügen"-Dialog. Ich werde einige Situationen anhängen, in denen der Profiler eine schlechte Leistung zeigt (diese Statistiken beziehen sich nicht auf einen Produktionsbuild).

System
MacBook Pro (Retina, 13 Zoll, Anfang 2015)
3,1 GHz Intel Core i7
Firefox 66.0.3

Fall 1 (Benutzer klickt auf einen Menüpunkt)
Renderdauer: 218 ms

Screen Shot 2019-05-10 at 4 45 26 AM

Screen Shot 2019-05-10 at 4 45 48 AM

Fall 2 (Benutzer klickt im Dialogfeld auf die Schaltfläche zur Bestellung hinzufügen)
Renderdauer: 356 ms

Screen Shot 2019-05-10 at 4 46 24 AM

Screen Shot 2019-05-10 at 4 47 10 AM

Ich bin mir sicher, dass ich hier einen Anfängerfehler mache, daher ist jede Anleitung sehr willkommen.

Da WithStyles(ButtonBase) neu gerendert wird, gehe ich davon aus, dass WithStyles einen Kontext verwendet, der neu erstellt wird, obwohl dies nicht erforderlich ist.

Ich habe es geschafft, dies https://github.com/mui-org/material-ui/blob/048c9ced0258f38aa38d95d9f1cfa4c7b993a6a5/packages/material-ui-styles/src/StylesProvider/StylesProvider.js#L38 zu finden, kann aber keinen Ort finden, an dem StylesProvider wird im tatsächlichen Code verwendet (die GitHubs-Suche ist nicht sehr gut), aber dies könnte der Grund sein.

Weiß @eps1lon , ob dies die Ursache sein könnte? Wenn dies der Fall ist, würde dies wahrscheinlich ein useMemo für das Kontextobjekt beheben. Obwohl ich nicht weiß, ob die localOptions stabil sind oder ob useMemo noch weiter verbreitet werden muss.

Vielleicht. Sie sollten jedoch zuerst untersuchen, warum Ihre Komponente mithilfe eines StylesProviders neu rendert. Dies ist entweder etwas oben in Ihrem Baum oder sollte sich an einer UI-Grenze befinden. In beiden Fällen sollten diese selten neu gerendert werden. Denken Sie daran, dass der Reaktionskontext ohnehin nicht für häufige Updates optimiert ist.

Wir sollten diese Dinge nicht vorzeitig optimieren, nur weil beim Rendern ein Objekt neu erstellt wird. Memoisierung ist kein Allheilmittel. Ohne konkretes Beispiel kann ich also nicht viel machen. Ja Dinge neu rendern. Manchmal öfter als nötig. Aber ein verschwendetes Re-Rendering bedeutet nicht, dass es die Ursache für einen Leistungsengpass ist.

@pytyl Ich habe mir Ihre Codesandbox angesehen, es gibt ein Problem mit der Rendering-Architektur. Sie rendern alles neu, wenn auf den Menüpunkt geklickt wird. Ihr GlobalContext überspringt die reine Logik.

@eps1lon Ich denke, wir sollten dieses Problem schließen. Es wäre besser, sich auf speziell identifizierte Probleme zu konzentrieren.

TL;DR: Kontext-Slices erstellen, Kontextwerte auswendig lernen, kein Problem speziell mit Material-UI: https://codesandbox.io/s/8lx6vk2978

Habe etwas gegraben und das Problem ist, dass Sie diesen großen globalen Kontext haben, der während des Renderns neu erstellt wird. Sie rendern Ihre App neu, wenn Sie klicken, woraufhin der globale Kontext neu erstellt wird. Ihr CategoryItem hört es, das 100 Mal in Ihrer App erscheint. Da Sie über 100 Material-UI-MenuItems verfügen, stoßen Sie auf den klassischen Tod durch tausend Schnitte.

Ironischerweise besteht ein Teil der Lösung darin, sich einen Kontextwert zu merken, aber der wichtige Teil besteht darin, separate Kontext-Slices zu identifizieren. Es scheint, dass ein Zustands- und Dispatch-Kontext angemessen ist. Dies wird empfohlen, wenn useContext mit useReducer verwendet wird und scheint hier zu passen.

Dies kann einen ziemlich großen Baum erstellen und Requisiten zur Hölle machen, je mehr Kontexte Sie haben. Ich ermutige Sie, sich useContext anzusehen. Es wird sehr helfen, wenn Sie sich diesen Problemen stellen.

@oliviertassinari Es ist gut, häufige Fallstricke mit Lösungen zu sammeln. Wir können entscheiden, ob wir daraus separate Issues erstellen wollen.

@oliviertassinari @eps1lon danke für die Überarbeitung! Leistung scheint großartig.

Ich hatte nur ein Problem mit der langsamen Rendering-Leistung. Ich habe es vollständig gelöst, indem ich alle Instanzen der <Box> Komponente durch <div> s ersetzt habe. Ich debuggte mit dem Reaction Devtools Flamegraph und ging von ungefähr 420 ms auf 20 ms.

Mit <Box> es;
Screen Shot 2019-08-16 at 12 47 25 AM

Ohne <Box> es:
Screen Shot 2019-08-16 at 12 42 38 AM

@mankittens Sie könnten die Box-Komponente https://github.com/mui-org/material-ui/pull/16858.

Ich schließe dieses Thema. Wir brauchen einen dedizierten Leistungsbericht für jeden potenziellen Verbesserungsbereich, keinen übergeordneten Thread.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen