Material-ui: Mejorar el rendimiento de Material-UI

Creado en 23 mar. 2018  ·  93Comentarios  ·  Fuente: mui-org/material-ui

En primer lugar, ¡muchas gracias por esta fantástica biblioteca de componentes! ¡Es genial!

Agregué un cajón en mi nueva aplicación. Sobre todo, copié el ejemplo del cajón. Solo por PoC multipliqué

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

sección.

Después de eso, se siente muy lento, especialmente en dispositivos móviles (nexus 4, cordova con paso de peatones 20). Utilizo el modo dev, pero el modo prod no se acelera mucho.

A través de las herramientas de desarrollo de reacción, noté que los componentes en mailFolderListItems se procesan en cada clic de enlace en react-router o incluso en el menú abierto. Se necesitan entre 50 y 60 ms para volver a procesar UN {mailFolderListItems} . yo suelo

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

Para eliminar la incertidumbre con otros componentes de la aplicación, convertí mailFolderListItems en Componente y deshabilité la reproducción:

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

    shouldComponentUpdate() {
        return false;
    }

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


                <Divider />
                <MailFolderListItems />

Después de eso, esta parte se siente bien.

Encontré https://github.com/mui-org/material-ui/issues/5628 . Sugiero volver a visitarlo . Optimizar shouldComponentUpdate es un paso fundamental y más importante para obtener rendimiento. PureComponent es el atajo más común.

Además, noté que se gasta mucho tiempo (1-2 ms y más para CADA componente de material-ui) en WithStyles .

  • [x] He buscado las ediciones de este repositorio y creo que esto no es un duplicado.

Comportamiento esperado

Espero obtener la mayor parte del rendimiento de reacción posible para esta gran biblioteca.

Comportamiento actual

La aplicación se vuelve más lenta con cada componente de material-ui.

Pasos para reproducir (para errores)

Todavía no proporciono un ejemplo de reproducción, porque acabo de copiar y pegar de la página de demostración del componente, pero si es necesario, puedo proporcionar la demostración de codesandbox. Para el navegador es notable, si el navegador se ralentiza en un factor> = 5 veces en la configuración de rendimiento.

Tu entorno

| Tech | Versión |
| -------------- | --------- |
| Material-UI | 1.0.0-beta.38 |
| Iconos de Material-UI | 1.0.0-beta.36 |
| Reaccionar | 16.2.0 |
| navegador | paso de peatones cordova 20 (es igual a android chrome 50) |

discussion performance

Comentario más útil

@oliviertassinari como gran desarrollador, ¿cómo puedes escribir cosas como esta? ¿Por qué utiliza _sus suposiciones personales_ como argumentos y no como hechos? Creo que presenté suficientes hechos arriba. Hice todo lo posible para demostrarlo, porque me gusta este proyecto y quiero contribuir. ¿No es suficiente? Ok, luego más hechos y _no_ suposiciones.

Solo para ti lo reduzco a 10 Botones. 10! Se significa cualquiera de no es utilizable! Probado en un dispositivo real con cruce de peatones 21 / chrome 51 sin acelerador:

Botón normal:
image

PureButton:
image

¡Esto sigue siendo una mejora de 8x! ¡Es enorme! ¿Te imaginas lo importante que es esto en un dispositivo móvil?

En lugar de 100 botones, encontrará 10 botones como máximo en una página. Aún así, encontrará 10 cuadrículas, 10 X, etc.

Usé Button porque es uno de los componentes más simples. Muestra que material-ui está roto desde el punto de vista del rendimiento. Ahora, imagine los componentes de un contenedor como AppBar, Toolbar, List, Drawer. ¡Esto es aún peor! Obtiene muy rápidamente 20 componentes / contenedores en cada página. Y el hecho de que no caduque la lentitud en su potente escritorio / mac no significa que material-ui no sea increíblemente lento.

react-intl, la comprobación entre los accesorios anteriores y nuevos siempre será falsa. Perderás ciclos de CPU. Entonces x13 -> x0.8

Muéstrame un ejemplo en codesandbox. No veo por qué debería suceder esto. Estoy seguro de que esto ocurre solo con el uso incorrecto de los componentes de reacción. Y el ejemplo oficial muestra un uso incorrecto, porque parece que react-intl no se aplica a los suscriptores de contexto . Pero hay muchos otros componentes que están alineados con las pautas de reacción y las mejores prácticas para mantener el rendimiento.

Por cierto: WithStyles consume hasta 2,27ms para el botón en el dispositivo móvil. 8 componentes y menos de 60 fps.

Todos 93 comentarios

Espero obtener la mayor parte del rendimiento de reacción posible para esta gran biblioteca.

@Bessonov Performance será un lanzamiento de enfoque posterior a la v1. Hemos intentado mantener el núcleo de la biblioteca lo más rápido posible. Debería ser lo suficientemente bueno para + 90% de los casos. Al menos, es mi experiencia usando la biblioteca hasta ahora. No ha proporcionado ninguna referencia externa, aparte de llamar nuestra atención sobre el hecho de que el rendimiento es importante, no podemos hacer que este problema sea procesable. Si puede identificar una limitación de la raíz de rendimiento de Material-UI que podamos investigar (con un código de reproducción, una caja o un repositorio), indíquelo. Hasta entonces, cierro el tema.

@oliviertassinari ¡ gracias por tu rápida respuesta! Estoy muy feliz de escuchar, esa actuación se enfocará después del lanzamiento.

Debería ser lo suficientemente bueno para + 90% de los casos. Al menos, es mi experiencia usando la biblioteca hasta ahora.

En el escritorio, sí, lo es. Pero en el móvil es realmente lento. Solo Drawer y algunos botones hacen que la aplicación se retrase. No responde y consume más energía según sea necesario.

No ha proporcionado ninguna referencia externa, aparte de llamar nuestra atención sobre el hecho de que el rendimiento es importante, no podemos hacer que este problema sea procesable.

Proporcioné una referencia al problema ya planteado y una referencia a la documentación de reacción.

Si puede identificar una limitación de la raíz de rendimiento de Material-UI que podamos investigar (con un código de reproducción y una caja o repositorio)

Como dije, puedo hacerlo. Aquí hay una comparación de componente puro y no puro. Los pasos para reproducir son:

  1. Use Chrome y vaya a https://codesandbox.io/s/r1ov818nwm
  2. Haga clic en "Abrir en una nueva ventana" en la ventana de vista previa.
  3. Abra las herramientas de desarrollo y vaya a la pestaña "rendimiento"
  4. Acelera tu CPU al menos 5 veces (dependiendo de tu PC, puede ser 10 veces) para reflejar el dispositivo móvil
  5. Haga clic en hamburguesa y vea cómo se retrasa.

Y ahora:

  1. Ir a index.js
  2. cambiar pure a true
  3. Ahorrar
  4. Repita los pasos de arriba
  5. ¡Disfrútala!

Este ejemplo es un poco poco realista porque he insertado demasiados elementos List. Pero como dije, en los dispositivos móviles se llega muy rápidamente al punto donde "sientes" cada acción.

Aquí está mi problema con WithStyles . Está en el escritorio con aceleración de la CPU. Me gustaría publicar capturas de pantalla desde un dispositivo real el lunes.

Hora para WithStyles(ListItem) :
image

Hora para ListItem :
image

La diferencia es de 1,26 ms solo para un componente ListItem . Con 13 componentes como este, ya no puede renderizar con 60 fps. Pero noté en Desktop, que esta duración no siempre está aquí.

Aquí hay una comparación de componente puro y no puro.

Por cierto, no digo que PureComponent sea LA solución para todos los problemas de rendimiento. Solo digo que puede ser uno de los ayudantes para hacer que material-ui se pueda usar en dispositivos móviles.

La diferencia es de 1,26 ms solo para un componente ListItem.

@Bessonov El componente WithStyles debería ser muy económico para instancias repetitivas del mismo componente. Solo inyectamos CSS una vez.
Tal vez esté alcanzando las limitaciones de React y el costo de componentes de orden superior.
Existe una variedad de soluciones para mitigar el problema de rendimiento en React. Por ejemplo, puede utilizar la memorización y virtualización de elementos. Mantengo este problema abierto como punto de partida para futuras investigaciones sobre el rendimiento.
No creo que podamos hacer mucho al respecto en este momento. Gracias por plantear la inquietud.

Ok, eso es solo una parte. ¿Qué opinas de la parte más importante: optimizar shouldComponentUpdate ?

EDITAR: Quiero dividir este problema en dos partes. Esta parte cuesta aproximadamente shouldComponentUpdate y la otra parte WithStyles , si puedo mostrar más información. ¿Está bien para ti?

optimización shouldComponentUpdate

@Bessonov No implementamos tal lógica a propósito en el lado de Material-UI por dos razones:

  1. La mayoría de nuestros componentes aceptan elementos de reacción, un cambio de referencia de elemento de reacción en cada renderizado, lo que hace que la lógica desperdicie sistemáticamente los ciclos de la CPU.
  2. Cuanto más cerca esté la lógica shouldComponentUpdate de la raíz del árbol de React, más eficiente será. Quiero decir, mientras más podas puedas hacer en el árbol, mejor. Los componentes de Material-UI son de bajo nivel. Hay poca oportunidad de

La única oportunidad que hemos encontrado es con los iconos. Háganos saber si puede identificar nuevos.

la otra parte para WithStyles

+ 90% del tiempo que se pasa en WithStyles en realidad se dedica a JSS , hay muy pocas cosas que podamos hacer al respecto en el lado de Material-UI.
Al menos, últimamente he estado identificando una oportunidad de almacenamiento en caché para la renderización del lado del servidor para mejorar en gran medida el rendimiento en la renderización repetitiva. Pero es solo del lado del servidor.

La mayoría de nuestros componentes aceptan elementos de reacción, un cambio de referencia de elemento de reacción en cada renderizado, lo que hace que la lógica desperdicie sistemáticamente los ciclos de la CPU.

Eso depende del uso y se puede mejorar. No me comparo, pero estoy seguro de que el uso correcto del componente y el shouldComponentUpdate optimizado (con una comparación superficial) es menos costoso que el componente no optimizado, especialmente para estructuras inmutables (y no se requiere inmutable.js por cierto). Boleto relacionado: https://github.com/mui-org/material-ui/issues/4305

Por ejemplo, en https://github.com/mui-org/material-ui/blob/v1-beta/docs/src/pages/demos/drawers/PermanentDrawer.js#L68 esto conduce a nuevos objetos y hace PureComponent sin sentido:

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

porque devuelve cada vez un nuevo objeto. Pero no:

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

Lo mismo para los componentes.

Cuanto más cerca esté la lógica shouldComponentUpdate de la raíz del árbol de React, más eficiente será. Quiero decir, mientras más podas puedas hacer en el árbol, mejor.

Sí, eso es verdad.

Los componentes de Material-UI son de bajo nivel. Hay poca oportunidad de apalancamiento.

Eso es parcialmente cierto. Como puede ver en https://codesandbox.io/s/r1ov818nwm , simplemente envuelvo List en PureComponent . Nada más y acelera el cajón en un factor significativo. Yo diría que esto es cierto para todos los componentes, al menos los que usan {this.props.children} . Creé otra demostración: https://codesandbox.io/s/my7rmo2m4y

Solo hay 101 botones (puro = falso) y botones envueltos en PureComponent (puro = verdadero, vea desde dónde se importa el botón). El mismo resultado del juego, si hago clic en el botón "Hacer clic":

Botón normal:
image

Botón envuelto (con gastos generales, etc.):
image

Como puede ver, ¡hay 637 ms frente a 47 ms para los botones normales! ¿Sigues pensando que optimizar shouldComponentUpdate (o solo PureComponent ) no vale la pena? :)

EDITAR: corregir la redacción al principio

@oliviertassinari @oreqizer Me di cuenta de algo interesante. Parece que la extensión PureComponent funciona mejor que Component con

  shouldComponentUpdate() {
    return false;
  }

image

EDITAR: Creo que este es uno de reaccionar optimización interna.

Como puede ver, ¡hay 637 ms frente a 47 ms para los botones normales! ¿Sigues pensando que optimizar shouldComponentUpdate (o simplemente PureComponent) no vale la pena? :)

@Bessonov Está demostrando el potencial de la lógica pura. ¡Sí, puede ser muy útil! Es una mejora x13. Pero no creo que esté cerca de las condiciones de la vida real:

  • En lugar de 100 botones, encontrará 10 botones como máximo en una página. Aún así, encontrará 10 cuadrículas, 10 X, etc.
  • En lugar de las propiedades simples proporcionadas a los elementos de nivel inferior, usará algo como react-intl, la comprobación entre los accesorios anteriores y nuevos siempre será falsa. Perderás ciclos de CPU . Entonces x13 -> x0.8 más o menos

@oliviertassinari como gran desarrollador, ¿cómo puedes escribir cosas como esta? ¿Por qué utiliza _sus suposiciones personales_ como argumentos y no como hechos? Creo que presenté suficientes hechos arriba. Hice todo lo posible para demostrarlo, porque me gusta este proyecto y quiero contribuir. ¿No es suficiente? Ok, luego más hechos y _no_ suposiciones.

Solo para ti lo reduzco a 10 Botones. 10! Se significa cualquiera de no es utilizable! Probado en un dispositivo real con cruce de peatones 21 / chrome 51 sin acelerador:

Botón normal:
image

PureButton:
image

¡Esto sigue siendo una mejora de 8x! ¡Es enorme! ¿Te imaginas lo importante que es esto en un dispositivo móvil?

En lugar de 100 botones, encontrará 10 botones como máximo en una página. Aún así, encontrará 10 cuadrículas, 10 X, etc.

Usé Button porque es uno de los componentes más simples. Muestra que material-ui está roto desde el punto de vista del rendimiento. Ahora, imagine los componentes de un contenedor como AppBar, Toolbar, List, Drawer. ¡Esto es aún peor! Obtiene muy rápidamente 20 componentes / contenedores en cada página. Y el hecho de que no caduque la lentitud en su potente escritorio / mac no significa que material-ui no sea increíblemente lento.

react-intl, la comprobación entre los accesorios anteriores y nuevos siempre será falsa. Perderás ciclos de CPU. Entonces x13 -> x0.8

Muéstrame un ejemplo en codesandbox. No veo por qué debería suceder esto. Estoy seguro de que esto ocurre solo con el uso incorrecto de los componentes de reacción. Y el ejemplo oficial muestra un uso incorrecto, porque parece que react-intl no se aplica a los suscriptores de contexto . Pero hay muchos otros componentes que están alineados con las pautas de reacción y las mejores prácticas para mantener el rendimiento.

Por cierto: WithStyles consume hasta 2,27ms para el botón en el dispositivo móvil. 8 componentes y menos de 60 fps.

¿Por qué utiliza sus suposiciones personales como argumentos y no como hechos?

¿Qué te hace pensar que es una suposición personal? Estaba tratando de realizar un pensamiento crítico. Conceptualmente hablando, el recorrido de prop adicional ralentizará la versión pura en comparación con la versión no pura, tiene que podar algo para que valga la pena. Mismo razonamiento que # 5628 o https://github.com/react-bootstrap/react-bootstrap/issues/633#issuecomment -234749417 o https://github.com/reactstrap/reactstrap/pull/771#issuecomment -375765577

Con puro:
capture d ecran 2018-03-27 a 11 43 41

Sin puro:
capture d ecran 2018-03-27 a 11 44 15

La reproducción es la siguiente.

@oliviertassinari ¿estás seguro de que codesandbox hace que todo esté bien para tu prueba? Porque mis resultados son muy diferentes:

Sin puro (incluso sin acelerador es lento):
image

Puro (después de cambiar a verdadero y guardar, obtengo una nueva URL para codesandbox):
image

Dado que no verifica los cambios de contexto y también obliga a los usuarios a asegurarse de que todos los componentes secundarios también sean "puros", no creo que esto sea adecuado para esta biblioteca. Esta biblioteca debe ser lo más flexible posible y creo que esto dificultará su uso.

Veo el punto. Pero es una compensación muy interesante. Por un lado, incluso material-ui debería ser "lo más flexible posible", pero por otro lado, el rendimiento actual lo hace _inutilizable_ en absoluto.

Quizás debería pensar en proporcionar una versión pura y no pura de los componentes. Lo hago ahora para que mi aplicación obtenga un rendimiento útil incluso en el escritorio.

@Bessonov El codeandbox no se guardó correctamente. Perdón. Faltaba el siguiente diff. He actualizado el enlace:

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

No entiendo por qué debería producir resultados diferentes. Obtengo un mejor gráfico, pero la versión no pura es significativamente más lenta.

EDITAR: ok, ya veo. Trate de averiguar qué está pasando ...

OK entiendo ahora. Simplemente el mismo "objeto nuevo en cada render" - algo. No lo había notado antes. En algunos casos, se puede mejorar con constantes a través del complemento de babel automáticamente.

Bueno, ¡entonces ya lo sabes! : D Aunque no hay mucho beneficio por sí solo (mostró ~ 7%), sigue siendo rentable en conclusión con componentes puros para evitar algunos defectos que mencionó anteriormente. ¡Lo probé ahora con Pure Wrapper + plugin babel + compilación de producción y es increíblemente rápido en el mismo dispositivo móvil!

Como dije, creo que es mejor ofrecer ambos: componentes no puros para la flexibilidad y componentes puros (los envoltorios son suficientes para mantenerlo simple y fácil de mantener) para el rendimiento. Pero para mí, puedo vivir solo con componentes puros, porque la mejora general del rendimiento es mucho mayor que los inconvenientes del rendimiento. O mejor: no puedo usar material-ui sin componentes puros.

Ok, por ahora estoy esperando más información sobre este tema y creo envoltorios propios en mi aplicación)

¡Gracias por tus ideas!

Nunca escuché que los elementos transform-react-constant-elements se usen y sean realmente útiles. Está bien incluir una microoptimización aleatoria, pero en la práctica rara vez se escribe un código lo suficientemente simple como para obtener mucho de él. Aunque, supongo que no sería una mala optimización para todos los componentes de iconos de estilo SVG como <Add /> .

Eche un vistazo a este ejemplo (haga clic en Complementos en el lateral, busque "react-constant" y haga clic en la casilla de verificación "transform-react-constant-elements") y verá que casi nada ha sido optimizado:

  • El simple estático InputAdornment se ha movido a la parte superior, yay.
  • Pero el botón de envío trivialmente simple ya no se optimizó en el momento en que le pusimos un controlador de eventos.
  • Y aunque el InputAdornment en sí mismo ahora está elevado, el InputProps={{startAdornment: ...}} todavía está en línea y crea un nuevo objeto en cada renderizado, lo que hace que PureComponent sea imposible.
  • Asimismo, classes={{label: classes.runButtonLabel}} hace imposible que PureComponent optimice el botón Ejecutar.

Personalmente, me gusta PureComponent y trato de usarlo absolutamente en todas partes y optimizar las cosas tanto como puedo para que funcione. Pero no parece que MUI esté hecho de una manera en que PureComponent funcionaría.

  • Los *Props props como InputProps son un patrón fundamental de cómo funciona MUI. No es solo una forma avanzada de modificar los componentes internos de MUI cuando lo necesita, sino algo que se usa regularmente en casos de uso simples. Este patrón a menudo hace que cualquier componente de la hoja que normalmente podría optimizarse en modo puro, no sea optimizable.
  • Del mismo modo, el patrón classes={{...}} tampoco funciona con PureComponent y es la forma de agregar cualquier estilo a las cosas en MUI. (Y decir que se use classes={classes} es de ninguna manera práctico porque un consumidor de la vida real probablemente tenga un nombre de clase diferente al de la clase interna del componente y es probable que classes también incluya clases destinadas a diseñar otros elementos en el mismo componente consumidor)
  • Los niños se usan mucho, en lugar de tener un componente, un patrón común a menudo usa 2-3 y solo uno de ellos puede optimizarse de forma pura si alguno de ellos puede.

Si queremos optimizar algo, estos problemas fundamentales deben resolverse; de ​​lo contrario, simplemente permitir que las personas habiliten una MUI en modo puro en realidad no optimizará mucho en absoluto. Puedo pensar en dos posibilidades.

Solo permitimos que se habilite el modo puro, los consumidores tienen que memorizar o levantar objetos manualmente para obtener optimizaciones

  1. Una forma en la que puedo pensar para hacer esto sería un poco menos de shallowMemoize que usa el this local y un key (para que pueda usarlo en diferentes bits de datos) que memoriza los datos siempre que sea superficial igual
  2. Otro en el que puedo pensar es en usar algo como reselect para crear selectores que memorizarán los datos.
  3. Otra forma de hacer el shallowMemoize en 1. sería pasarlo a render() con un decorador. De esta manera, podríamos pasar uno nuevo en cada renderizado y en lugar de necesitar key podemos simplemente verificar si alguno de los objetos memorizados del último renderizado debe reutilizarse, luego descartar todos los valores antiguos.

El problema, por supuesto, es que esto hace que los consumidores sean mucho más grandes y desordenados y requiere que la lógica se eleve manualmente a lugares donde está lejos del código que lo usa.

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>
    );
  }

En lugar de intentar optimizar InputProps, clases, etc., animamos a las personas a crear microcomponentes para todos sus casos de uso.

Si esta es la forma recomendada de usar MUI, es posible que ni siquiera necesitemos un modo puro. Como puede ver, tan pronto como comience a crear pequeños componentes auxiliares para sus casos de uso comunes, esos componentes en sí pueden ser fácilmente componentes puros. En el ejemplo, WeightTextField ahora nunca se vuelve a renderizar siempre que value siga siendo el mismo, omitiendo por completo los estilos, cualquier trabajo de memorización necesario para InputProps o la configuración de InputAdornment. Y cuando value cambia, tenemos que volver a renderizar TextField de todos modos para que el InputProps={{...}} no puro no importe.

Estoy bien con este camino. Me gustan los microcomponentes en teoría; aunque odio cada sintaxis / patrón actualmente válido para escribirlos que se me ocurre. No quiero MyComponent = enhance(MyComponent) , quiero decorarlos, pero no puedes decorar ninguna de las formas cortas de escribir un componente diminuto. Tampoco me gusta convertir import {TextField} from 'material-ui'; en import WeightTextField from '../../../ui/WeightTextField ; `.

`` js
let WeightTextField = ({unidad, InputProps, ... props}) => (
{...accesorios}
InputProps = {{
startAdornment:{unidad} ,
... InputProps
}}
onChange = {ejemplo}
valor = {ejemplo} />
);
WeightTextField = pure (WeightTextField);

RunButton = componer (
withStyles (theme => ({
etiqueta: {
fontWeight: '800',
},
})),
puro
)(Botón);

const SubmitButton = pure (Botón);

class FormPage extiende Componente {
estado = {ejemplo: ''};

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

render () {
const {título} = this.props;
const {ejemplo} = this.state;

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

}
}
`` ``

Tengo un caso de uso en el que necesito mostrar entre 500 y 2000 casillas de verificación en una página de una lista grande. Al usar casillas de verificación nativas del navegador, el rendimiento es bueno, pero al usar el componente <Checkbox> , el rendimiento es muy deficiente y escala linealmente con el número de casillas de verificación en la página. Ejemplo: https://codesandbox.io/s/5x596w5lwn

Estoy usando mui @ next , ¿hay alguna estrategia que pueda emplear _ahora_ para que esto funcione?

@wilsonjackson
Primero, no hagas lo siguiente. Esto creará un nuevo controlador en cada casilla de verificación en cada renderizado, lo que desoptimizará cualquier optimización de PureComponent que intente hacer.

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

En segundo lugar, cree su propio componente pequeño para envolver la casilla de verificación y hacer que ese componente sea puro. Esto tiene el beneficio adicional de que puede agregar cualquier propiedad que sea común a todas sus casillas de verificación. Y, dado que tiene el problema común de necesitar un controlador de eventos de cambio diferente para cada elemento, podemos usar un componente de clase y hacerlo en el componente en lugar de en el contenedor de lista.

https://codesandbox.io/s/r7l64j6v5n

Si queremos optimizar algo, estos problemas fundamentales deben resolverse; de ​​lo contrario, simplemente permitir que las personas habiliten una MUI en modo puro en realidad no optimizará mucho en absoluto. Puedo pensar en dos posibilidades.

@dantman Estas opciones de API se han hecho para mejorar el DX tanto como sea posible mientras se intenta ser lo suficientemente rápido.

En lugar de intentar optimizar InputProps, clases, etc., animamos a las personas a crear microcomponentes para todos sus casos de uso.

Sí. El patrón de envoltura es definitivamente la forma recomendada de personalizar la biblioteca. Se puede ampliar para aplicar optimizaciones de rendimiento. Es más fácil en la tierra del usuario donde la variabilidad del uso de los componentes es mucho menor. Incluso podríamos agregar una sección de preguntas frecuentes o una guía sobre este punto en la documentación.

Sí. El patrón de envoltura es definitivamente la forma recomendada de personalizar la biblioteca. Se puede ampliar para aplicar optimizaciones de rendimiento. Es más fácil en la tierra del usuario donde la variabilidad del uso de los componentes es mucho menor. Incluso podríamos agregar una sección de preguntas frecuentes o una guía sobre este punto en la documentación.

Está bien. En ese caso:

  • Queremos recomendar bibliotecas como recompose que facilitan la escritura de pequeños componentes sin estado. Diablos, agradecería que alguien me dijera que hay otro patrón o biblioteca que es similar a recomponer pero hace que la construcción de funciones sin estado puras como los patrones de recomposición, pero es tan agradable como usar la sintaxis del decorador
  • Al igual que enumeramos sugerencias sobre cómo usar las diversas bibliotecas de autocompletar y css en bibliotecas js con MUI, probablemente también querremos sugerir los diversos patrones de organización de carpetas existentes y bibliotecas de pirateo de rutas (las que le permiten convertir importaciones de ../../../../ui/Foo en algo como something-local/Foo ) que se puede usar para hacer que el uso de su propia serie local de microcomponentes que envuelvan MUI sea tan agradable como usar import {TextField} from 'material-ui'; y no se sienta como una regresión en la facilidad del desarrollador .

@dantman fantástico, gracias.

He necesitado varias veces para aplicar sCU debido a que withStyles (o más bien JSS) es muy lento. No conozco el código de JSS, pero parece que podría optimizarse bastante. Por lo general, uso componentes con estilo o glamorosos y, por lo tanto, termino con JSS y uno del otro en la aplicación y ambos superan a JSS.

Si bien estos casos pueden ser un poco molestos, es fácil solucionarlos con sCU a nivel de aplicación o actualizaciones de estado más inteligentes. Todavía no he visto que un solo componente MUI sea lo suficientemente lento como para ser problemático y todavía tengo que codificar realmente dentro de MUI para tomar un tiempo significativo.

No quiere decir que no podría ser más rápido y seguro que sería mejor si se necesitara menos supervisión, pero al menos por lo que veo, sería mejor dedicar tiempo a optimizar JSS directamente que MUI en ese caso.

@Pajn Gracias por los comentarios. Sería realmente genial ver alguna situación en la que el rendimiento de withStyles sea problemático o donde sea superado por los componentes con estilo.

¿Alguien revisó este repositorio https://github.com/reactopt/reactopt ?

$ 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

Estos componentes hacen un renderizado innecesario solo con un simple clic, ¿por qué no intentar el ciclo de vida de Debería Actualizar el Componente?

@ nimaa77

¿Alguien revisó este repositorio https://github.com/reactopt/reactopt ?
No, utilizo las capacidades de las herramientas de desarrollo de Chrome / React y por qué actualizaste .

Estos componentes hacen un renderizado innecesario solo con un simple clic, ¿por qué no intentar el ciclo de vida de Debería Actualizar el Componente?

Consulte la discusión anterior. Esto no se ajusta a todos los casos de uso. Para mí, parece que material-ui debería ofrecer ambos: versiones puras y no puras de componentes. Veo una gran mejora en el rendimiento con componentes puros.

@dantman

Eche un vistazo a este ejemplo (haga clic en Complementos en el lateral, busque "react-constant" y haga clic en la casilla de verificación "transform-react-constant-elements") y verá que casi nada ha sido optimizado:

Esta es solo una forma de abordar este problema. También hay otras opciones, complementos y otras optimizaciones hechas a mano. No me malinterpretes, pero no me interesan las discusiones teóricas sobre qué optimizaciones son buenas o malas. Estoy interesado en la optimización práctica que FUNCIONA. Al menos en mi configuración. Todo lo que escribí anteriormente está FUNCIONANDO para mí y hace que mi aplicación al menos se pueda usar en dispositivos móviles de medios bajos. Aunque me veo obligado a realizar más optimizaciones, como reordenar árboles de componentes, el rendimiento logrado no sería posible sin componentes puros y otras optimizaciones automáticas. Y sí, perfilo y optimizo mucho para hacer esto.

@Bessonov tal vez podamos usar un accesorio para hacer que el método shouldComponentUpdate haga una comparación superficial (https://reactjs.org/docs/shallow-compare.html) O siempre devuelva falso,
esto no aumentará el tamaño del paquete con dos versiones de componentes (puro y normal)

@lucasljj No espero un aumento significativo del tamaño del paquete si esto se hace como un contenedor para un componente con estado como se menciona arriba. Consulte también los perfiles anteriores: solo los componentes puros son más rápidos que return false; en sCU.

El problema con los componentes puros o los componentes que implementan sCU es si usa un componente no puro en su interior o children . Consulte las declaraciones anteriores. Otro tema a abordar es el cambio de tema. No probado, pero creo que esto se puede superar al menos con Context API.

Los componentes puros de @bossonov se consideran los que deberían haber sido por defecto de reacción que no lo son porque llegaron tarde a la fiesta. Pero estoy de acuerdo en que la mayoría de la gente creará envoltorios de estilo de formulario redux mitigando la falta de este en el subárbol

el problema al que hace referencia con respecto a los componentes puros y los niños, solo ocurre si los accesorios de nivel superior no se propagan a los niños. Cada accesorio o cambio de estado provocará una repetición a través del árbol secundario hasta el nivel en el que el accesorio no se propague.
Porque la idea de componentes puros es que se reproduce en cada cambio de estado o prop. No conozco los aspectos internos íntimamente, pero me preguntaba si no podría usar una propiedad de cambio por componente que le pasa a todos los niños. Incluso puede usar la nueva api de contexto para este propósito para no tener que pasar un accesorio a través de todo el árbol.

@oliviertassinari

El rendimiento será un tema de enfoque posterior al lanzamiento de la versión 1.

Genial, se lanzó v1 :) ¿Hay alguna idea de cuándo debería abordarse el rendimiento? Me di cuenta de que este problema no forma parte del hito posterior a la versión 1.

@Bessonov Creo que sería genial dedicar algo de tiempo a actualizar el ROADMAP. Respecto a la actuación. Tengo dos cosas en mente procesables, pero espero descubrir más:

  1. No podemos mejorar algo que no podemos ver. Necesitamos un punto de referencia. He visto dos bibliotecas interesantes hasta ahora:
  2. Renderizar el lado del servidor CSS requiere ~ 30% de lo que React necesita renderizar. Porque no admitimos style = f(props) (bueno, todavía no lo admitimos: # 7633). Podemos implementar una capacidad de almacenamiento en caché muy eficiente, reduciendo el costo cerca del 0% para solicitudes repetidas. Podría trabajar en eso para la oficina si el desempeño de SSR daña nuestras métricas comerciales.
    Pero no son las únicas opciones, también podemos pensar en complementos de Babel para aplicar los costosos ajustes preestablecidos de JSS en el momento de la compilación en lugar de en el tiempo de ejecución (para equilibrar las implicaciones del tamaño del paquete) cc @kof.

Gracias por las URL.

Totalmente de acuerdo con 1 y vinculado # 4305 arriba.

  1. SSR puede ayudar con la carga de la primera página, pero no ayuda con la reproducción lenta o cordova. Si bien, personalmente, puedo ignorar la primera carga en el escritorio o incluso en un dispositivo móvil, la reproducción lenta aún afecta a los dispositivos móviles después de la primera carga.

Las optimizaciones del tiempo de compilación serían excelentes y, desde mi perspectiva, deberían preferirse, ya que pueden mejorar la carga y la reproducción por primera vez.

Otra cosa es una especie de documentación o proyecto de ejemplo sobre cómo usar mui (y babel, etc.) para obtener un mejor rendimiento.

No sé si se puede usar jss-cache .

La canalización de preprocesamiento ISTF + podrá reducir algunos ms.

El sábado 19 de mayo de 2018 a las 19:06, Anton Bessonov [email protected] escribió:

Gracias por las URL.

Totalmente de acuerdo con 1 y vinculado # 4305
https://github.com/mui-org/material-ui/issues/4305 arriba.

  1. SSR puede ayudar con la carga de la primera página, pero no ayuda con la
    rendición o cordova. Aunque personalmente puedo ignorar la primera carga en
    escritorio o incluso móvil, la reproducción lenta todavía afecta a los dispositivos móviles después
    primera carga.

Las optimizaciones de tiempo de compilación serían geniales y, desde mi perspectiva, deberían
preferible, ya que puede mejorar la carga y la reproducción por primera vez.

Otra cosa es una especie de documentación o proyecto de ejemplo sobre cómo usar mui
(y babel, etc.) para obtener un mejor rendimiento.

No sé si jss-cache http://cssinjs.org/jss-cache?v=v3.0.0 puede ser
usó.

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/mui-org/material-ui/issues/10778#issuecomment-390418709 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AADOWGrCxNGqrT4MijiX8r9Ad32z6RsJks5t0FEtgaJpZM4S4woq
.

Puede ser útil para configurar puntos de referencia:
https://github.com/A-gambit/CSS-IN-JS-Benchmarks/blob/master/RESULT.md

Oh, react-jss (que es usado por material-ui) parece ser bastante lento

@janhoeck no lo es, y no podrás demostrar lo contrario, estoy seguro.

@kof Entiendo bastante que los micro-benchmarks no dicen mucho, pero 4x-6x en comparación con afrodita es realmente lento. De hecho, el rendimiento general de material-ui no es brillante.

4x-6x en comparación con afrodita es realmente lento

@Bessonov La metodología de referencia
https://twitter.com/necolas/status/954024318308007937?lang=fr

Los resultados en mi navegador:
⚠️ con la incertidumbre

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

el rendimiento general del material-ui no es brillante

Es muy posible que estemos limitados por React y el costo de los componentes.

@Bessonov, sea ​​lo que sea que usó para verificar el rendimiento, cuando se trata de afrodita no es cierto, porque usa lo antes posible y retrasa la representación, por lo que la mayoría de los puntos de referencia miden el tiempo de la CPU, pero no el rendimiento final de la representación. Dicho esto, el banco @oliviertassinari publicado es más realista.

Con respecto al rendimiento de MUI, puede ser más rápido cuando no usa ninguna abstracción, pero ese no es el punto. MUI siguiente es bastante rápido. Es tan rápido que nunca debe preocuparse por su rendimiento a menos que USTED haya hecho algo completamente incorrecto o haya utilizado mal la biblioteca.

@kof no, MUI no es rápido por defecto. Nos vemos obligados a implementar soluciones alternativas para obtener un rendimiento aceptable. Seguro: ¿no has visto que se trata de dispositivos móviles y no de Mac de gama alta?

nunca se preocupe por su rendimiento a menos que USTED haya hecho algo completamente incorrecto o haya utilizado mal la biblioteca

Bueno, arriba hay algunos ejemplos de codesanbox. Si solo usamos componentes MUI en un contenedor y guardamos los valores de entrada en el estado del contenedor, como se mostró en las demostraciones de componentes, entonces se vuelve inutilizable muy rápido. Envuelva en PureComponent, use componentes no controlados, reordenamiento de árboles, elementos constantes, memorice, etc., ayudándonos a mantener un rendimiento aceptable. Le invitamos a mostrar cómo usar MUI correctamente, para obtener mejores resultados, por ejemplo en (atm, ya no usamos cajón):
https://codesandbox.io/s/r1ov818nwm

@oliviertassinari gracias por el enlace. Estos son los resultados para Chrome 66 en el nexus 4. Como puede ver, los resultados son 10 veces más pobres. Creo que en crosswalk 21 / chrome 51 puede ser un poco más lento:

screenshot_2018-06-12-18-20-19

Es muy posible que estemos limitados por React y el costo de los componentes.

En mi humilde opinión, no necesariamente, como lo han mencionado otros también. Cada repetición evitada es una gran victoria para el rendimiento y la batería. No olvide que el rendimiento es el tercer punto de su hoja de ruta: +1:

Véalo de esa manera: si lo que hace con cssinjs es demasiado lento en un dispositivo móvil, no debería usarlo para esta reacción y la mayoría de las otras cosas tampoco. Si cssinjs lo ralentiza, el componente de reacción lo ralentiza mucho más. Necesita repensar el nivel de abstracción o aplicar optimizaciones.

@oliviertassinari es eso evtl. ¿Es posible que el contenedor mui vuelva a reproducir los estilos jss en la actualización? Puede haber alguna fuga potencialmente que haga un trabajo innecesario.

@kof

Míralo así [...]

Te entiendo. Pero los componentes de reacción y reacción (puros) no son un cuello de botella por sí mismos y funcionan muy bien. Por ejemplo, MUI no usa PureComponent (con los pros y los contras descritos anteriormente), pero son nuestras vidas más seguras. @oliviertassinari mencionó que existe la posibilidad de obtener un mejor rendimiento mediante el uso de cachés o precompilador.

No me malinterpreten, son chicos increíbles y estoy muy contento con cada lanzamiento de MUI: aplauso: Pero también debe considerar el rendimiento, porque no todos los usuarios visitan sitios web a través de computadoras de escritorio de alto rendimiento .

si reaccionar no es un cuello de botella de rendimiento y está seguro de ello, JSS tampoco lo será. Lo único que pude imaginar que necesita ser validado si hay algún trabajo innecesario y css se regenera en la actualización.

El componente puro es algo que USTED debería usar en su código de nivel de aplicación, si necesita una optimización. MUI no tiene que hacerlo, es una biblioteca genérica y no debe hacer suposiciones. PureComponent no siempre es bueno.

JSS ha considerado el rendimiento desde el primer día y pasé horas interminables en micro optimizaciones.

Esto es ridículo. No puedo evitar que los componentes de la interfaz de usuario del material se vuelvan a renderizar en cada interacción. No importa lo que haga, incluso algo tan simple como esto:

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

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

Activa nuevas renderizaciones de los elementos de tipografía subyacentes. Como es esto posible ?

Oye, esto solo sucede con la lista y el elemento de la lista. Por qué ? ¿Hay algo que pueda hacer?

@ danielo515 Difícil de decir sin el ejemplo completo. Los componentes List también están usando el contexto, por lo que si también está cambiando ese valor, también activará la reproducción.

Entiendo que @ eps1lon. Sin embargo, ¿podría explicar cómo puede ocurrir un cambio de contexto? No paso nada al componente de lista, solo renderizo una pérdida de niños dentro de él.

@ danielo515 Básicamente, cada vez que el List reproduce. La nueva API de contexto habilitó algunas estrategias de optimización que podríamos explorar al memorizar el valor del contexto.

Actualmente, la API de contexto reacciona y desencadena una repetición en cada consumidor si el valor cambia WRT a igualdad estricta. Dado que creamos un nuevo objeto en cada llamada de renderizado a List cada consumidor también volverá a renderizar.

Entonces, por ahora, un aumento de rendimiento fácil sería ajustar el List para que no se vuelva a procesar con tanta frecuencia. Sin embargo, recomendaría hacer puntos de referencia primero o abandonar el renderizado antes. Las llamadas de renderización repetidas Typography no deberían ser tan malas.

Envolver componentes con React.memo funciona bien para cualquier cosa sin hijos. Tengo un formulario con 3 ExpansionPanel y algunos 14 FormControl y se está quedando atrás en el escritorio.

Sin una solución para estos problemas de rendimiento, no podré seguir usando material-ui: s

@prevostc es difícil saber qué está mal sin un ejemplo.

@prevostc sin ver una reproducción de codesandbox, ni nosotros ni usted podemos saber si esto está relacionado con este tema

@prevostc Cree sus propios componentes que no requieran hijos que luego pueda memorizar.

es decir, cree su propio componente MyExpansionPanel puro / memorizado que tome accesorios de datos / eventos, pero no elementos secundarios, y sea responsable de representar un solo panel de expansión. Luego use ese <MyExpansionPanel ... /> para renderizar cada uno de sus paneles de expansión. Luego, las re-renderizaciones se limitarán a un solo panel de expansión (o dos cuando se realiza la transición entre dos).

@oliviertassinari @kof @dantman
Aquí hay una reproducción de codeandbox de mi problema de rendimiento: https://codesandbox.io/s/yvv2y2zxxx

Es un formulario con ~ 20 campos (no es infrecuente), experimentará cierto retraso en la entrada del usuario. En dispositivos más lentos, este formulario simplemente no se puede utilizar.

El problema de rendimiento proviene de la repetición masiva en la entrada del usuario, pero envolver los componentes MUI en un componente puro (React.memo) no hace nada, ya que todo aquí tiene hijos y niños, esto obligará a volver a renderizar AFAIK (fuente: https: // reactjs.org/docs/react-api.html#reactpurecomponent)

A continuación se muestran algunas capturas de pantalla de mi punto de referencia manual, una sin ninguna optimización, una con todo memorizado y otra que usa un componente de entrada personalizado con un estado local para evitar establecer el estado en el formulario completo con demasiada frecuencia.
En cada configuración, una reconciliación de entrada de usuario toma alrededor de 60 ms (muy por encima de los 16 ms que necesito para renderizar a 60 fps.

Tenga en cuenta que con mucho gusto me enteraría de que hice algo mal porque amo a MUI y estaría triste si no hubiera una solución fácil <3

@dantman ¿Cómo se puede escribir un ExpansionPanel que no tome ningún React.ReactNode como entrada (hijos o accesorios)? Si quiere decir que debería escribir un componente específico para cada panel de mi aplicación, lamentablemente esto no es posible, tengo demasiados.

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 ¿Cómo se puede escribir un ExpansionPanel que no tome ningún React.ReactNode como entrada (hijos o accesorios)? Si quiere decir que debería escribir un componente específico para cada panel de mi aplicación, lamentablemente esto no es posible, tengo demasiados.

Sí, en lugar de hacer un solo componente masivo con un árbol de paneles profundamente anidado, separe las piezas del panel de expansión en componentes. Es imposible optimizar árboles masivos como ese.

No es imposible. Los componentes de React son muy ligeros. Copie y pegue un bloque de código de un componente masivo en una función y ya casi termina, después de eso solo tiene que conectar los accesorios. Y mientras Reaccione, recuerde esa función y evite pasar cosas que rompan la optimización pura de los accesorios, entonces las cosas se optimizan con bastante facilidad.

Recuerde, si está creando un componente pequeño para romper un trozo de un componente enorme. No tiene por qué ser algo complejo con su propio archivo y su propia validación de prop. Puede ser un componente funcional simple en el mismo archivo que el componente en el que se usa sin propTypes. es decir, puede crear componentes pequeños que solo están disponibles para el componente en el mismo archivo que ellos.

Sí, Material UI es un poco más lento que los elementos dom de bajo nivel. Pero incluso si un MUI Input fuera 10 veces más lento que un input procesar y las cosas se volvieran demasiado lentas después de 100. Entonces todavía tienes un problema incluso sin MUI porque incluso si es 10 veces más rápido, el input procesar

@prevostc Aquí está su demostración optimizada al dividir el panel de expansión en un pequeño componente del mismo archivo. Como puede ver, solo se vuelve a renderizar un panel de expansión cuando se actualiza una entrada. No se pierde tiempo volviendo a renderizar paneles de expansión no relacionados. Si quisiera, incluso podría hacer algo similar a las entradas que comparten un patrón similar, lo que da como resultado que las entradas no relacionadas tampoco se vuelvan a renderizar.

Notaré que este no es solo un buen patrón de optimización, es un buen patrón de codificación. En código real, donde no está creando una matriz de entradas, sino escribiéndolas explícitamente con nombres, etiquetas y propósitos definidos. Encontrar límites y patrones repetidos no solo optimiza las cosas, sino que también reduce la repetición, lo que hace que el código sea más SECO y legible. es decir, en lugar de InputWrapper probablemente rompería ese combo FormControl + InputLabel + FormHelperText + Input en un pequeño componente local SimpleTextInput. Lo que no solo lo optimizaría (dando como resultado entradas no relacionadas que no se volverían a renderizar) sino que también significaría que el código no necesita repetir el texto estándar adicional.

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

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

Al leer esto, llegué a la conclusión de que para optimizar mui es necesario crear componentes específicos más pequeños. Eso es algo de lo que ya me di cuenta y probé con éxito. Sin embargo, también entendí que no hay forma de optimizar los componentes de la lista porque la API de contexto cambia todos los accesorios de entrada.

Saludos

Ok, aquí hay una prueba de esfuerzo actualizada https://codesandbox.io/s/wz7yy1kvqk

Estoy de acuerdo con @dantman en este punto genérico https://github.com/mui-org/material-ui/issues/10778#issuecomment -449153635 PERO no esperaba tal problema de rendimiento con esta baja cantidad de componentes, pero tenga paciencia yo ya que encontré la fuente de mi problema de rendimiento.

¿JSS es lento? (alerta de spoiler: no)

En referencia a algunos de los comentarios anteriores en este hilo, agregué una casilla de verificación en la prueba de esfuerzo para eliminar todas las llamadas a withStyles y llegué a la conclusión de que JSS es rápido y no es la fuente del problema de rendimiento. (como @kof lo señaló en https://github.com/mui-org/material-ui/issues/10778#issuecomment-396609276).

screenshot 2018-12-22 at 15 17 26

La solución

Para mi caso de uso específico, pude señalar que el problema era que cada entrada de formulario se volvía a representar en la actualización del formulario, aunque solo una entrada realmente cambió.
En la captura de pantalla a continuación, envolví tanto el FormControl como el Input en un componente memorizado, evitando renderizar si el valor no cambiaba. @dantman en realidad sugirió que creara un componente específico para cada ExpansionPanel pero esta es una solución mucho menos genérica. Como nota al margen, cada panel todavía se vuelve a renderizar y el rendimiento está lejos de ser óptimo, pero es suficiente por ahora.

screenshot 2018-12-22 at 15 18 22

¿Entonces? ¿Que sigue?

Creo que no hay forma de evitar este tipo de problemas con un cambio en el código material-ui sin un cambio masivo en la API actual que depende en gran medida de la composición de React.ReactNode .
Pero como @dantman mencionó en https://github.com/mui-org/material-ui/issues/10778#issuecomment -449153635, MUI es un poco más lento de lo que esperaba de él. No abordar esto en absoluto es un error en mi humilde opinión.

Teniendo en cuenta este problema, es posible que debamos crear una página de documento relacionada con los problemas de rendimiento y cómo abordarlos. Incluso si su página redirigirá principalmente al documento oficial (https://reactjs.org/docs/optimizing-performance.html) y enumerará los componentes que pueden causar problemas de rendimiento, es un comienzo.
Sería mejor consolar. Advertir al usuario cuando ocurra tal problema, pero no puedo encontrar una manera de detectar problemas en el nivel de material de interfaz de usuario.

@prevostc este mensaje me

Yo no: s

No sé lo suficientemente interno de MUI como para tener alguna idea de cómo mejorar su rendimiento en bruto (sin cambio de API) en este momento. Estoy trabajando en algunas ideas, pero nada concluyente a partir de hoy: tengo una aplicación en la que un grupo de radio está re-renderizado cuando su padre directo no lo es, no puedo reproducir esto localmente todavía.

Cualquier cambio de API sería considerar eliminar cualquier React.ReactNode props de la API (niños y otros props como icon, etc.) pero no pude encontrar una buena manera de mantener la misma configurabilidad.
Esto es lo que probé: https://codesandbox.io/s/jpw36jw65 (advertencia: no terminado).

También es algo a tener en cuenta que MUI es especialmente lento en el modo de desarrollo debido a que reacciona siendo especialmente lento en el modo de desarrollo. No sé si hay forma de mejorar esto.

¿Hay algún progreso en la adición de capacidades (como la que sugirió @Bessonov ) para optimizar los problemas de rendimiento que actualmente enfrenta Material-UI?
Cuando comenzamos a usar esta gran biblioteca para nuestro proyecto, no sabía que estos problemas de rendimiento pueden ocurrir cuando el proyecto crece cada vez más; Además, no vi ninguna sección en los documentos de Material-UI que me informara sobre los casos que pueden hacer que Material-UI se vuelva lento y dañe la UX.
En este número se informan muchos problemas de rendimiento, directa o indirectamente relacionados con Material-UI. Creo que será una buena idea incluirlos en otro número para que todos puedan seguir el progreso. Si cree que está bien, puedo abrir una nueva edición.

@ mkermani144 Todavía tenemos que ver un informe de rendimiento directamente relacionado con algo que Material-UI está haciendo mal. Hasta ahora, este número se ha utilizado como un foro de ayuda para las personas que luchan. ¿Confirma que no se informó nada procesable? Voy a decir lo obvio, la abstracción de React tiene un costo . Cada componente que envuelve un host agregará peso a su árbol de renderizado, lo ralentizará. Si bien puede representar más de 100 elementos de lista con un host nativo, comienza a ser un problema cuando los envuelve con un componente de clase. No es específico de Material-UI.

Mesa

Modo de desarrollo

Tomemos el ejemplo de una mesa. Es un componente que la gente encuentra lento. Hemos documentado la virtualización , ayuda mucho.

En el siguiente caso de prueba, renderizamos 100 elementos en modo dev . Podemos considerar los siguientes casos:

  1. Tabla Raw: https://codesandbox.io/s/v066y5q7z3 : 63ms en el render
  2. Material de la tabla-UI Master: https://codesandbox.io/s/m4kwmvj9ly : 250ms en el render
  3. Material de la tabla-UI Siguiente: https://codesandbox.io/s/2o35yny1jn : 262ms en el render

Entonces, la sobrecarga de usar Material-UI en modo dev sobre el elemento host es de alrededor de x4 (¡la diferencia es menor en producción!), Simplemente porque creamos componentes intermedios. Es por eso que la virtualización comienza a ser importante después de renderizar una lista de ~ 100 elementos de la tabla. Ahora, podemos sumergirnos un poco en ¿Por qué y qué podemos hacer al respecto?

  1. Table Raw + componente de función. ¿Por qué un componente funcional? Queremos abstraer los nombres de clase utilizados. No queremos que los usuarios del componente los repitan.
    https://codesandbox.io/s/1zl75mwlpj : 105ms en el render
  2. Tabla Raw + componente de función + forwardRef. ¿Por qué forwardRef? Queremos que el componente sea "transparente", pudiendo acceder al elemento host con una ref.
    https://codesandbox.io/s/32o2y0o9op : 120ms en el render
  3. Table Raw + componente de función + forwardRef + withStyles. ¿Por qué con estilos? Porque queremos diseñar nuestro componente:
    https://codesandbox.io/s/j2n6pv768y : 200ms en el render
  4. Tabla Raw + componente de función + forwardRef + makeStyles. makeStyles puede ser más rápido que withStyles, intentemos:
    https://codesandbox.io/s/yw52n07l3z : 130ms en el render.

Entonces, tenemos un apalancamiento disponible: migrar todos los componentes de withStyles a makeStyles. Podemos ganar alrededor de + 30% de rendimiento (262 / (262 - 70)) en modo dev .

Modo de producción

He ejecutado los mismos casos de prueba en modo de producción:

  • n ° 1 30ms en el hidrato
  • n ° 3 106ms en el hidrato
  • n ° 4 40ms en el hidrato
  • n ° 5 41ms en el hidrato
  • n ° 6 80ms en el hidrato
  • n ° 7 57ms en el hidrato

Entonces, la migración de withStyles a makeStyles sigue siendo una aceleración del + 30% en teoría en el modo de producción .

Si cree que está bien, puedo abrir una nueva edición.

@ mkermani144 Si tiene un caso específico en el que Material-UI lo está haciendo mal, seguro.

Leí sobre todos los comentarios debajo de este problema. Mi problema no encaja en ninguno de los otros mencionados anteriormente.

Tengo un componente List que contiene algunos ListItem s, y tengo uno de ellos seleccionado y resaltado. Cuando hago clic en otro ListItem para seleccionarlo y resaltarlo, la lista completa (que contiene sus elementos secundarios) se vuelve a renderizar.

Sé que este problema puede parecer exactamente el mismo que un comentario anterior , pero no lo es; Al menos eso pienso.

Veamos los resultados de React Profiler:

image
Como puede ver, tengo un componente MyList en el nivel superior de la imagen. Este componente es solo un envoltorio de MUI List y lo hace puro, es decir:

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

Agregué este contenedor porque uno de sus comentarios mencionó que volver a renderizar List hace que el contexto se actualice, y esta actualización de contexto hace que todos los consumidores (incluido ListItem s) también se vuelvan a renderizar .

También intenté hacer puros todos mis ListItem s y volví a crear un perfil de la aplicación. Los resultados fueron los mismos, excepto que mi componente personalizado (es decir, MyListItem ) no se volvió a representar _self_, pero todos los niños debajo de él __did__.

Sé que el problema se debe a que el contexto que usa MUI para el estilo vuelve a renderizar _ de alguna manera_. Pero no sé por qué ocurre esta re-renderización y cómo puedo evitarla.

¿O estoy haciendo algo mal?

Nota: uso la nueva solución de estilo MUI (alfa), es decir, @material-ui/styles . No sé si esto importa.

@ mkermani144 Elimina Material-UI con elementos nativos, observa que la re-renderización todavía está presente. La lógica pura no ayudará así. React.createElement crea nuevas referencias en cada render, invalida su PureComponent.

Sí, sé que los elementos son objetos y los objetos no son estrictamente iguales en Javascript, por lo que sCU falla.

Pero no entiendo lo que quiere decir cuando dice que React.createElement se llama de nuevo. ¿Qué llamada a createElement ? Si te refieres a las llamadas dentro de List , solo llama a createElement para sus hijos ( ListItem s) solo si se vuelve a renderizar. Si no se vuelve a renderizar, no se llamará a createElement y no se debe volver a renderizar. El problema es que el List sí se vuelve a renderizar.

@ mkermani144 Si puede crear un ejemplo de reproducción mínima, podemos echarle un vistazo.

Su MyList (y por lo tanto List ) se vuelve a generar debido al componente que representa MyList (llamémoslo MyComponent ) se vuelve a generar. PureComponent en MyList no ayuda ya que MyComponent han vuelto a generar y se han creado nuevos hijos por MyList modo que el cheque de MyList s falla.

Su MyComponent probablemente se vuelva a generar porque allí es donde almacena el estado del artículo seleccionado.

Creo que la implementación de MUI de List debería cambiar para no recrear el valor de contexto de List en cada renderizado
aquí: https://github.com/mui-org/material-ui/blob/fb4889f42613b05220c49f8fc361451066989328/packages/material-ui/src/List/List.js#L57

Entonces, en su lugar, haga que List se vea algo como esto:

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>
  );
});

Eso simplificaría la creación de versiones sCU de ListItems

Su MyList (y por lo tanto, List) se vuelve a generar debido a que el componente que representa MyList (llamémoslo MyComponent) se vuelve a generar. PureComponent en MyList no ayuda, ya que MyComponent se ha vuelto a generar y se han creado nuevos elementos secundarios para MyList para que la comprobación de MyLists falle.

@Pajn No, mira los resultados de mi MyList no se volvió a renderizar (es gris), pero List sí (es azul). No persisto en PureComponent por MyList . Aunque implemento sCU por MyList para que no se vuelva a renderizar, List __ se vuelve a renderizar__.

@oliviertassinari
Creé un ejemplo de reproducción mínima:

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;

Reaccionar los resultados del generador de perfiles (después de hacer clic en el cuarto elemento de la lista):

image

Como puede ver, funciona como se esperaba, es decir, no hay re-renderizaciones adicionales (excepto por los componentes ButtonBase dentro de ListItem s). El problema es que esta reproducción es _demasiado mínima_; hay muchas cosas que me salto.

Sé que no puede decirme qué está mal con mi código, lo que provoca re-renderizaciones adicionales. Pero te hago una pregunta: ¿Qué puede causar una re-renderización en los componentes WithStylesInner que envuelven los componentes MUI?

@ mkermani144 ¿Qué opinas de esta solución?

--- 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>

¿Quieres enviar una solicitud de extracción? :) Estamos usando la misma estrategia con el componente Table. Funciona muy bien. Gracias por informar del problema.

@oliviertassinari Seguro. Esto es exactamente lo que sugirió @Pajn anteriormente. Envié un PR .

14934 se fusiona; el componente List , sin embargo, puede convertirse en un cuello de botella de rendimiento si es lo suficientemente grande, sin importar cuánto esté optimizado. ¿No deberíamos proporcionar un ejemplo que muestre el uso de react-window o react-virtualized con el componente List , como el que tenemos en Table docs?

¿No deberíamos proporcionar un ejemplo que muestre el uso de react-window o react-virtualized con el componente List, como el que tenemos en los documentos de la tabla?

Eso sería genial: +1:

Luego, construí una aplicación de chat y la aplicación necesita generar una gran lista de contactos. Me encontré con el mismo problema
@ mkermani144 tenía.

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

@ henrylearn2rock ¿Ha considerado utilizar la virtualización? Hemos agregado una demostración para la lista: https://next.material-ui.com/demos/lists/#virtualized -list.

Esto también realmente me hizo tropezar. Creo que la mayoría de la gente (incluyéndome a mí) asumió que todo bajo un componente puro estaba a salvo de una repetición, lo que evidentemente no es el caso de esta biblioteca. Voy a probar la virtualización como sugirió recientemente. ¡Gracias!

Creo que la mayoría de la gente (incluyéndome a mí) asumió que todo bajo un componente puro estaba a salvo de una repetición, lo que evidentemente no es el caso de esta biblioteca.

No es así como funciona React.PureComponent o React.memo. Solo afecta al componente en sí. Es posible que los niños aún tengan que volver a renderizar si el contexto cambia.

@pytyl ¿Puedes compartir el código donde usaste un PureComponent y esperabas que evitara cualquier re-renderización en su subárbol?

@ eps1lon la siguiente documentación hace que parezca que devolver falso desde shouldComponentUpdate omite automáticamente la re-renderización en los componentes secundarios.
https://reactjs.org/docs/optimizing-performance.html#shouldcomponentupdate -in-action

Dado que shouldComponentUpdate devolvió falso para el subárbol enraizado en C2, React no intentó representar C2 y, por lo tanto, ni siquiera tuvo que invocar shouldComponentUpdate en C4 y C5.

¿Quizás estoy equivocado en esto? La siguiente es una instantánea de mi generador de perfiles. Solo por el simple hecho de probar, devuelvo explícitamente falso para shouldComponentUpdate en mi componente Menú:

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

Esto hizo que todos mis componentes secundarios (Categorías, Categoría, Artículos de categoría, Artículo de categoría) no se volvieran a renderizar. Muchas cosas relacionadas con MUI parecen volver a renderizarse en la parte inferior, lo que parece estar causando mucho retraso. Cosas como estilos, tipografía, ButtonBase. Todavía es un poco nuevo en React, así que disculpe mi ignorancia. A continuación se muestra mi código para el componente Menú (donde devuelvo falso para shouldComponentUpdate):

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);

Necesitaría un codeandbox completo para comprender el problema.

@ eps1lon Intentaré producir uno para mañana. Gracias.

@ eps1lon aquí está el codepen:
https://codesandbox.io/s/348kwwymj5

Breve descripción
Es una aplicación de menú básica para restaurantes (que a menudo tienen más de 100 elementos de menú). Cuando el usuario hace clic en un elemento del menú, se abre un cuadro de diálogo "agregar al pedido". Adjuntaré algunas situaciones en las que el generador de perfiles muestra un rendimiento deficiente (estas estadísticas no están en una compilación de producción).

Sistema
MacBook Pro (Retina, 13 pulgadas, principios de 2015)
Intel Core i7 de 3,1 GHz
Firefox 66.0.3

Caso 1 (el usuario hace clic en un elemento del menú)
Duración del renderizado: 218ms

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

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

Caso 2 (el usuario hace clic en el botón Agregar al pedido en el cuadro de diálogo)
Duración del renderizado: 356ms

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

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

Estoy seguro de que estoy cometiendo un error de principiante aquí, por lo que cualquier orientación es muy apreciada.

Como WithStyles (ButtonBase) se vuelve a generar, supongo que WithStyles usa un contexto que se recrea aunque no es necesario.

Me las arreglé para encontrar esto https://github.com/mui-org/material-ui/blob/048c9ced0258f38aa38d95d9f1cfa4c7b993a6a5/packages/material-ui-styles/src/StylesProvider/StylesProvider.js#L38 pero no puedo encontrar un lugar donde StylesProvider se usa en código real (la búsqueda de GitHubs no es muy buena) pero podría ser la razón.

¿Sabe @ eps1lon si esta podría ser la causa? Si es así, un useMemo en el objeto de contexto probablemente solucionaría esto. Aunque no sé si las localOptions son estables o si useMemo necesita propagarse aún más.

Quizás. Pero primero debe investigar por qué su componente que usa un StylesProvider se reproduce. Esto es algo en la parte superior de su árbol o debería estar en algún límite de la interfaz de usuario. En cualquier caso, esos rara vez deberían rendirse. Recuerde que el contexto de reacción no está optimizado para actualizaciones frecuentes de todos modos.

No deberíamos optimizar prematuramente estas cosas solo porque algún objeto se vuelve a crear durante el procesamiento. La memorización no es una solución milagrosa. Entonces, sin un ejemplo concreto, no puedo hacer mucho. Sí, las cosas se vuelven a renderizar. A veces más a menudo de lo necesario. Pero una repetición desperdiciada no significa que sea la causa de un cuello de botella en el rendimiento.

@pytyl He mirado su codeandbox, hay un problema con la arquitectura de renderizado. Renuncia todo cuando se hace clic en el elemento del menú. Su GlobalContext salta la lógica pura.

@ eps1lon Creo que deberíamos cerrar este tema. Sería mejor centrarse en cuestiones específicamente identificadas.

TL; DR: cree segmentos de contexto, memorice valores de contexto, no hay problema con material-ui específicamente: https://codesandbox.io/s/8lx6vk2978

Investigué un poco y el problema es que tienes este gran contexto global que se vuelve a crear durante el renderizado. Vuelve a renderizar su aplicación cuando hace clic, momento en el que se vuelve a crear el contexto global. Su CategoryItem lo está escuchando y aparece 100 veces en su aplicación. Dado que tiene 100 elementos de menú de material-ui, se encuentra con la muerte clásica por mil cortes.

Irónicamente, parte de la solución es memorizar un valor de contexto, pero la parte importante es identificar segmentos de contexto separados. Parece que un contexto de estado y envío es apropiado. Esto se recomienda cuando se usa useContext con useReducer y parece encajar aquí.

Esto puede crear un árbol bastante grande y convertir los accesorios en un infierno cuantos más contextos tenga. Te animo a que eches un vistazo a useContext . Será de gran ayuda si comienza a enfrentar estos problemas.

@oliviertassinari Es un buen tema recopilar errores comunes con soluciones. Podemos decidir si queremos crear problemas separados de él.

@oliviertassinari @ eps1lon ¡ gracias por revisar! El rendimiento parece genial.

Acabo de tener un problema con el rendimiento de renderizado lento. Lo resolví por completo reemplazando todas las instancias del componente <Box> con <div> s. Realicé la depuración usando el flamegraph de react devtools, y pasé de alrededor de 420 ms a 20 ms.

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

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

@mankittens Puede mantener el componente Box, utilizando componentes con estilo como motor de estilo. El rendimiento sería mucho mejor. Debería mejorar con JSS en un futuro próximo https://github.com/mui-org/material-ui/pull/16858.

Estoy cerrando este tema. Necesitamos un informe de desempeño específico para cada área potencial de mejora, no un hilo general.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

anthony-dandrea picture anthony-dandrea  ·  3Comentarios

mattmiddlesworth picture mattmiddlesworth  ·  3Comentarios

sys13 picture sys13  ·  3Comentarios

zabojad picture zabojad  ·  3Comentarios

ryanflorence picture ryanflorence  ·  3Comentarios