Métodos para mejorar y optimizar el rendimiento en aplicaciones React

Publicado: 2022-03-10
Resumen rápido ↬ Desde que se introdujo React, ha transformado la forma en que los desarrolladores front-end crean aplicaciones web, y su DOM virtual es famoso por representar componentes de manera efectiva. En este tutorial, analizaremos varios métodos para optimizar el rendimiento en aplicaciones React y también las características de React que podemos usar para mejorar el rendimiento.

React permite que las aplicaciones web actualicen sus interfaces de usuario (IU) rápidamente, pero eso no significa que su aplicación React mediana o grande funcionará de manera eficiente. Su desempeño dependerá de cómo use React cuando lo construya, y de su comprensión de cómo funciona React y el proceso a través del cual viven los componentes a través de las diversas fases de su ciclo de vida. React ofrece muchas mejoras de rendimiento para una aplicación web, y puede lograr estas mejoras a través de diversas técnicas, funciones y herramientas.

En este tutorial, analizaremos varios métodos para optimizar el rendimiento en aplicaciones React y también las características de React que podemos usar para mejorar el rendimiento.

¿Por dónde empezar a optimizar el rendimiento en una aplicación React?

No podemos comenzar a optimizar una aplicación sin saber exactamente cuándo y dónde optimizar. Te estarás preguntando, "¿Por dónde empezamos?"

Durante el proceso de renderizado inicial, React construye un árbol DOM de componentes. Entonces, cuando los datos cambian en el árbol DOM, queremos que React vuelva a renderizar solo aquellos componentes que se vieron afectados por el cambio, omitiendo los otros componentes en el árbol que no se vieron afectados.

Sin embargo, React podría terminar volviendo a renderizar todos los componentes en el árbol DOM, aunque no todos se vean afectados. Esto dará como resultado un mayor tiempo de carga, tiempo perdido e incluso recursos de CPU desperdiciados. Tenemos que evitar que esto suceda. Entonces, aquí es donde enfocaremos nuestro esfuerzo de optimización.

En esta situación, podríamos configurar cada componente para renderizar o diferenciar solo cuando sea necesario, para evitar desperdiciar recursos y tiempo.

¡Más después del salto! Continúe leyendo a continuación ↓

Medición de desempeño

Nunca inicies el proceso de optimización de tu aplicación React en base a lo que sientes. En su lugar, utilice las herramientas de medición disponibles para analizar el rendimiento de su aplicación React y obtenga un informe detallado de lo que podría estar ralentizándola.

Análisis de los componentes de React con la pestaña de rendimiento de Chrome

De acuerdo con la documentación de React, mientras aún está en modo de desarrollo, puede usar la pestaña "Rendimiento" en el navegador Chrome para visualizar cómo se montan, actualizan y desmontan los componentes de React. Por ejemplo, la siguiente imagen muestra la pestaña "Rendimiento" de Chrome perfilando y analizando mi blog en modo de desarrollo.

Resumen del perfilador de rendimiento
Resumen del generador de perfiles de rendimiento (vista previa grande)

Para hacer esto, siga estos pasos:

  1. Deshabilite todas las extensiones temporalmente, especialmente React Developer Tools, porque pueden alterar el resultado del análisis. Puede deshabilitar fácilmente las extensiones ejecutando su navegador en modo de incógnito.
  2. Asegúrese de que la aplicación se esté ejecutando en modo de desarrollo. Es decir, la aplicación debería estar ejecutándose en su host local.
  3. Abra las herramientas para desarrolladores de Chrome, haga clic en la pestaña "Rendimiento" y luego haga clic en el botón "Grabar".
  4. Realice las acciones que desea perfilar. No grabes más de 20 segundos, o Chrome podría bloquearse.
  5. Detener la grabación.
  6. Los eventos de React se agruparán bajo la etiqueta "User Timing".

Los números del generador de perfiles son relativos. La mayoría de los tiempos y componentes se renderizarán más rápidamente en producción. Sin embargo, esto debería ayudarlo a determinar cuándo la IU se actualiza por error, así como con qué profundidad y con qué frecuencia ocurren las actualizaciones de la IU.

Perfilador de herramientas para desarrolladores de React

De acuerdo con la documentación de React, en react-dom 16.5+ y react-native 0.57+, las capacidades mejoradas de creación de perfiles están disponibles en modo desarrollador utilizando React Developer Tools Profiler. El generador de perfiles utiliza la API del generador de perfiles experimental de React para recopilar información de tiempo sobre cada componente que se procesa, con el fin de identificar los cuellos de botella de rendimiento en una aplicación React.

Simplemente descargue React Developer Tools para su navegador, y luego puede usar la herramienta de generación de perfiles que se incluye con él. El generador de perfiles solo se puede usar en el modo de desarrollo o en la versión de generación de perfiles de producción de React v16.5+. La siguiente imagen es el resumen del perfilador de mi blog en modo de desarrollo usando React Developer Tools Profiler:

Reaccionar Herramientas para desarrolladores Profiler flamegraph
React Developer Tools Profiler flamegraph (vista previa grande)

Para lograr esto, sigue estos pasos:

  1. Descarga las herramientas para desarrolladores de React.
  2. Asegúrese de que su aplicación React esté en modo de desarrollo o en la compilación de creación de perfiles de producción de React v16.5+.
  3. Abra la pestaña "Herramientas para desarrolladores" de Chrome. Una nueva pestaña llamada "Perfilador" estará disponible, proporcionada por React Developer Tools.
  4. Haga clic en el botón "Grabar" y realice las acciones que desea perfilar. Idealmente, deje de grabar después de haber realizado las acciones que desea perfilar.
  5. Aparecerá un gráfico (conocido como flamegraph) con todos los controladores de eventos y componentes de su aplicación React.

Nota : Consulte la documentación para obtener más información.

Memoización con React.memo()

React v16 se lanzó con una API adicional, un componente de orden superior llamado React.memo() . Según la documentación, esto existe solo como una optimización del rendimiento .

Su nombre, " memo ", proviene de memorización, que es básicamente una forma de optimización utilizada principalmente para acelerar el código almacenando los resultados de las llamadas a funciones costosas y devolviendo el resultado almacenado cada vez que se vuelve a llamar a la misma función costosa .

La memorización es una técnica para ejecutar una función una vez, generalmente una función pura, y luego guardar el resultado en la memoria. Si intentamos ejecutar esa función nuevamente, con los mismos argumentos que antes , solo devolverá el resultado previamente guardado de la ejecución de la primera función, sin ejecutar la función nuevamente.

Asignando la descripción anterior al ecosistema de React, las funciones mencionadas son componentes de React y los argumentos son accesorios.

El comportamiento predeterminado de un componente declarado usando React.memo() es que se procesa solo si los accesorios en el componente han cambiado. Hace una comparación superficial de los accesorios para verificar esto, pero hay una opción disponible para anular esto.

React.memo() aumenta el rendimiento de una aplicación React al evitar volver a renderizar componentes cuyas propiedades no han cambiado o cuando no es necesario volver a renderizar.

El siguiente código es la sintaxis básica de React.memo() :

 const MemoizedComponent = React.memo((props) => { // Component code goes in here })

Cuándo usar React.memo()

  • Componente funcional puro
    Puede usar React.memo() si su componente es funcional, recibe los mismos accesorios y siempre genera el mismo resultado. También puede usar React.memo() en componentes no puramente funcionales con ganchos React.
  • El componente se renderiza a menudo
    Puede usar React.memo() para envolver un componente que se procesa con frecuencia.
  • El componente se vuelve a renderizar con los mismos accesorios.
    Use React.memo() para envolver un componente que generalmente se proporciona con los mismos accesorios durante la re-renderización.
  • Elementos medios a altos
    Úselo para un componente que contenga un número medio a alto de elementos de la interfaz de usuario para verificar la igualdad de accesorios.

Nota : tenga cuidado al memorizar componentes que utilizan accesorios como devoluciones de llamada. Asegúrese de utilizar la misma instancia de función de devolución de llamada entre representaciones. Esto se debe a que el componente principal podría proporcionar diferentes instancias de la función de devolución de llamada en cada procesamiento, lo que provocaría la interrupción del proceso de memorización. Para solucionar esto, asegúrese de que el componente memorizado siempre reciba la misma instancia de devolución de llamada.

Veamos cómo podemos usar la memorización en una situación del mundo real. El componente funcional a continuación, llamado "Foto", usa React.memo() para evitar que se vuelva a renderizar.

 export function Photo({ title, views }) { return ( <div> <div>Photo title: {title}</div> <div>Location: {location}</div> </div> ); } // memoize the component export const MemoizedPhoto = React.memo(Photo);

El código anterior consta de un componente funcional que muestra un div que contiene el título de una foto y la ubicación del sujeto en la foto. También estamos memorizando el componente creando una nueva función y llamándola MemoizedPhoto . Memorizar el componente de la foto evitará que el componente se vuelva a renderizar siempre que los elementos de utilería, el title y la location sean los mismos en las representaciones posteriores.

 // On first render, React calls MemoizedPhoto function. <MemoizedPhoto title="Effiel Tower" location="Paris" /> // On next render, React does not call MemoizedPhoto function, // preventing rendering <MemoizedPhoto title="Effiel Tower" location="Paris" />

Aquí, React llama a la función memorizada solo una vez. No renderizará el componente en la próxima llamada siempre que los accesorios sigan siendo los mismos.

Agrupación y Minificación

En las aplicaciones React de una sola página, podemos agrupar y minimizar todo nuestro código JavaScript en un solo archivo. Esto está bien, siempre y cuando nuestra aplicación sea relativamente pequeña.

A medida que crece nuestra aplicación React, agrupar y minimizar todo nuestro código JavaScript en un solo archivo se vuelve problemático, difícil de entender y tedioso. También afectará el rendimiento y el tiempo de carga de nuestra aplicación React porque estamos enviando un gran archivo JavaScript al navegador. Por lo tanto, necesitamos algún proceso que nos ayude a dividir la base del código en varios archivos y enviarlos al navegador en intervalos según sea necesario.

En una situación como esta, podemos usar algún tipo de paquete de activos como Webpack y luego aprovechar su funcionalidad de división de código para dividir nuestra aplicación en varios archivos.

La división de código se sugiere en la documentación de Webpack como un medio para mejorar el tiempo de carga de una aplicación. También se sugiere en la documentación de React para la carga diferida (sirviendo solo las cosas que el usuario necesita actualmente), lo que puede mejorar drásticamente el rendimiento.

Webpack sugiere tres enfoques generales para la división de código:

  • Puntos de entrada
    Dividir manualmente el código usando la configuración de entrada.
  • Prevención de duplicación
    Utilice SplitChunksPlugin para eliminar duplicados y dividir fragmentos.
  • Importaciones dinámicas
    Código dividido a través de llamadas de función en línea dentro de los módulos.

Beneficios de la división de código

  • La división del código ayuda con los recursos de caché del navegador y con el código que no cambia con frecuencia.
  • También ayuda al navegador a descargar recursos en paralelo, lo que reduce el tiempo total de carga de la aplicación.
  • Nos permite dividir el código en fragmentos que se cargarán a pedido o según lo necesite la aplicación.
  • Mantiene la descarga inicial de recursos en el primer renderizado relativamente pequeña, lo que reduce el tiempo de carga de la aplicación.
Proceso de empaquetado y minificación
Proceso de agrupación y minificación (vista previa grande)

Estructuras de datos inmutables

La documentación de React habla del poder de no mutar los datos. Cualquier dato que no se puede cambiar es inmutable. La inmutabilidad es un concepto que los programadores de React deberían entender.

Un valor u objeto inmutable no se puede cambiar. Entonces, cuando hay una actualización, se crea un nuevo valor en la memoria, dejando intacto el antiguo.

Podemos usar estructuras de datos inmutables y React.PureComponent para verificar automáticamente un cambio de estado complejo. Por ejemplo, si el estado en su aplicación es inmutable, en realidad puede guardar todos los objetos de estado en una sola tienda con una biblioteca de administración de estado como Redux, lo que le permite implementar fácilmente la funcionalidad de deshacer y rehacer.

No olvide que no podemos cambiar los datos inmutables una vez creados.

Beneficios de las estructuras de datos inmutables

  • No tienen efectos secundarios.
  • Los objetos de datos inmutables son fáciles de crear, probar y usar.
  • Nos ayudan a escribir la lógica que se puede usar para verificar rápidamente las actualizaciones en el estado, sin tener que verificar los datos una y otra vez.
  • Ayudan a evitar el acoplamiento temporal (un tipo de acoplamiento en el que el código depende del orden de ejecución).

Las siguientes bibliotecas ayudan a proporcionar un conjunto de estructuras de datos inmutables:

  • ayudante de inmutabilidad
    Mute una copia de los datos sin cambiar la fuente.
  • Inmutable.js
    Las colecciones de datos persistentes inmutables para JavaScript aumentan la eficiencia y la simplicidad.
  • inmutable
    Las estructuras de datos inmutables para JavaScript se vuelven compatibles con las matrices y objetos de JavaScript normales.
  • Reaccionar-copiar-escribir
    Esto proporciona un estado inmutable con una API mutable.

Otros métodos para mejorar el rendimiento

Use una compilación de producción antes de la implementación

La documentación de React sugiere usar la compilación de producción minimizada al implementar su aplicación.

Advertencia de "compilación de producción" de React Developer Tools
Advertencia de "compilación de producción" de React Developer Tools (vista previa grande)

Evite las funciones anónimas

Debido a que a las funciones anónimas no se les asigna un identificador (a través de const/let/var ), no son persistentes cada vez que un componente inevitablemente se vuelve a representar. Esto hace que JavaScript asigne nueva memoria cada vez que este componente se vuelve a renderizar, en lugar de asignar una sola pieza de memoria solo una vez, como cuando se usan funciones con nombre.

 import React from 'react'; // Don't do this. class Dont extends Component { render() { return ( <button onClick={() => console.log('Do not do this')}> Don't </button> ); } } // The better way class Do extends Component { handleClick = () => { console.log('This is OK'); } render() { return ( <button onClick={this.handleClick}> Do </button> ); } }

El código anterior muestra dos formas diferentes de hacer que un botón realice una acción al hacer clic. El primer bloque de código usa una función anónima en la propiedad onClick() , y esto afectaría el rendimiento. El segundo bloque de código usa una función con nombre en la función onClick() , que es la forma correcta en este escenario.

Montar y desmontar componentes a menudo es costoso

El uso de condicionales o tenarios para hacer desaparecer un componente (es decir, para desmontarlo) no es recomendable, porque el componente hecho para desaparecer hará que el navegador vuelva a pintar y redistribuir. Este es un proceso costoso porque las posiciones y geometrías de los elementos HTML en el documento deberán recalcularse. En su lugar, podemos usar las propiedades de opacity y visibility de CSS para ocultar el componente. De esta manera, el componente seguirá estando en el DOM pero invisible, sin ningún costo de rendimiento.

Virtualizar Listas Largas

La documentación sugiere que si está representando una lista con una gran cantidad de datos, debe representar una pequeña porción de los datos en la lista a la vez dentro de la ventana gráfica visible. Luego, puede generar más datos a medida que se desplaza la lista; por lo tanto, los datos se muestran solo cuando están en la ventana gráfica. Este proceso se llama "ventana". En las ventanas, se representa un pequeño subconjunto de filas en un momento dado. Hay bibliotecas populares para hacer esto, dos de las cuales son mantenidas por Brian Vaughn:

  • ventana de reacción
  • reaccionar-virtualizado

Conclusión

Existen varios otros métodos para mejorar el rendimiento de su aplicación React. Este artículo ha discutido los métodos más importantes y efectivos de optimización del rendimiento.

Espero que hayas disfrutado leyendo este tutorial. Puede obtener más información a través de los recursos que se enumeran a continuación. Si tiene alguna pregunta, déjela en la sección de comentarios a continuación. Estaré encantado de responder a cada uno de ellos.

Referencias y recursos relacionados

  • "Optimización del rendimiento", React Docs
  • “Use React.memo sabiamente”, Dmitri Pavlutin
  • “Técnicas de optimización del rendimiento en React”, Niteesh Yadav
  • “Inmutabilidad en React: No hay nada de malo en mutar objetos”, Esteban Herrera
  • “10 formas de optimizar el rendimiento de su aplicación React”, Chidume Nnamdi
  • “5 consejos para mejorar el rendimiento de sus aplicaciones React”, William Le