Análisis y optimización de aplicaciones React.

Las personas como yo que luchan por sitios de alto rendimiento a menudo pasan mucho tiempo en esto. Así que ahora voy a resolver de una vez por todas el problema de los recursos web de baja velocidad cuya interfaz está escrita en React. A saber, sugiero que todos los que lean esto dejen de usar React hoy.



El autor del material, cuya traducción publicamos hoy, por supuesto, bromea. Aquí hablaremos sobre cómo optimizar el rendimiento de las aplicaciones React. Por cierto, antes de comenzar, pensemos por qué generalmente se necesita la optimización del sitio web. Quizás podamos decir que es necesario para que el sitio sea utilizado por más personas que antes de la optimización.

Introduccion


¿Cómo optimizar el sitio? ¿Cómo puede evaluar los beneficios de la optimización para los usuarios del sitio? ¿Y por qué necesita pensar en tales indicadores?

Intentaremos responder a estas preguntas buscando la forma más fácil de crear aplicaciones React, utilizando la herramienta create-react-app (CRA). Un nuevo proyecto creado con esta herramienta tiene las siguientes dependencias:

  • La biblioteca de react principal, que le permite trabajar con componentes React: 2.5 Kb.
  • La biblioteca react-dom , que permite que los componentes se muestren en la página, convirtiéndolos en estructuras adecuadas para su inserción en el árbol DOM: 30.7 Kb.
  • Una pequeña cantidad de código, que incluye la plantilla del primer componente: aproximadamente 3 Kb.

Estos datos se obtienen para React 16.6.3.

Para saber cuánto tiempo llevará descargar una nueva aplicación CRA en su teléfono Moto G4, puede usar el servicio WebPageTest .


Tiempo de carga del sitio web en Moto G4 usando diferentes redes

Esta aplicación, una variación de "Hello, World", está alojada en el alojamiento Firebase, se está investigando cargándola en el navegador Chrome utilizando tres tipos de conexiones:

  • 4G (9 Mbps)
  • 3G (1.6 Mbps)
  • Conexión 3G lenta (400 Kbps)

Aquí debe considerar los retrasos en la red.

¿Por qué usé el Moto G4 para el experimento? Este es un teléfono simple y económico, similar a aquellos teléfonos que, en forma de dispositivos básicos, son utilizados por muchas personas en los países en desarrollo. En una red 4G, la aplicación se cargó en 2 segundos. En una red 3G lenta, la página tardó más de 4 segundos en conectarse.

Aunque estas métricas son bastante interesantes, no son particularmente útiles si no sabe quiénes son sus usuarios. Lo que usted define como "lento" puede ser completamente diferente de lo que yo o alguien más considera "lento". Y su percepción de la carga "rápida" del sitio puede verse distorsionada por el dispositivo y la conexión de red que utiliza. Si incluye una computadora de escritorio conectada a Internet a través de una conexión por cable en este experimento, puede ver cuán grande puede ser la diferencia entre cargar un sitio "rápido" y "lento".


Tiempo de carga del sitio en la computadora de escritorio y Moto G4

Con el fin de mejorar el rendimiento de las aplicaciones React, cuya construcción utiliza la biblioteca React, como se dice, "lista para usar", se describen mejoras de React DOM que apuntan a simplificar algunas cosas. Por lo tanto, el sistema de eventos contiene muchos polyfills, que, para muchos navegadores nuevos, no son necesarios, y el equipo de desarrollo está considerando opciones para eliminarlos o simplificarlos, si es posible. Puedes verlo aquí .

¿Puedo medir el nivel actual de rendimiento del sitio web?


Una aplicación típica de React puede contener muchos componentes y bibliotecas de terceros. Esto significa que el rendimiento de la aplicación "Hello, World" no nos proporciona información particularmente valiosa sobre cómo se cargan las aplicaciones reales. ¿Hay alguna manera de averiguar qué tan alto rendimiento son la mayoría de los sitios creados con alguna tecnología (como React)?

El recurso HTTP Archive puede ayudarnos a responder esta pregunta. Esta es una plataforma de código abierto que se centra en observar cómo se construye la web. Esto se realiza rastreando mensualmente millones de sitios, analizándolos mediante WebPageTest y registrando información sobre ellos. Esta información incluye el número de consultas, las métricas relacionadas con la carga de datos, los tamaños de los datos transmitidos y otros indicadores.

Aquí hay otra herramienta interesante: una extensión para Chrome llamada Library-Detector-for-Chrome . Le permite averiguar qué bibliotecas de JavaScript se utilizan en la página. Recientemente se ha incluido como una herramienta de auditoría de página en Lighthouse. Esto sugiere que la información que proporciona esta extensión se puede obtener para muchos sitios cuya información se almacena en el Archivo HTTP. Esto puede ayudar a aquellos que desean analizar los resultados de las pruebas de miles de sitios que usan una biblioteca de JavaScript específica (el mecanismo para determinar React está aquí ).

El conjunto completo de datos del Archivo HTTP está disponible públicamente y se puede encontrar en BigQuery . Después de investigar 140,000 sitios usando React para cargarlos en un entorno móvil simulado artificialmente (conjunto de datos 2019_01_01 ), logramos descubrir lo siguiente:

  • La mediana del primer indicador de pintura significativa (primera pantalla significativa, tiempo para dibujar elementos importantes) fue de 6,9 ​​s.
  • La mediana del indicador Tiempo para interactuar (tiempo para interactividad, tiempo de carga de elementos de interacción) fue de 19,7 s.

Puede explorar estos datos usted mismo.

El usuario tarda casi 20 segundos en comenzar a interactuar con el sitio. Y esto, en general, está sucediendo, por así decirlo, aquí y ahora, aunque puede parecer inverosímil. Esto se puede ver en sitios grandes cuando se trabaja con ellos desde dispositivos débiles en líneas de comunicación lentas. Además, ahora me gustaría expresar varias razones por las cuales estos datos deberían considerarse con cierto grado de escepticismo.

  • Hay muchos factores que influyen en el rendimiento del sitio. Entre ellos, la cantidad de código JavaScript enviado al usuario, la cantidad de imágenes y otros materiales que se muestran en la página, etc. Comparará incorrectamente el rendimiento de cualquier sitio basado en React con el rendimiento de una página etiquetada "Hola, Mundo" si no se tienen en cuenta otros factores.
  • Si intenta obtener los datos reemplazando el nombre de otra biblioteca en la solicitud React, obtendrá números muy similares.

Con el fin de obtener datos precisos sobre qué tipo de rendimiento demuestran de manera realista los sitios existentes que utilizan una determinada biblioteca, se necesita mucho trabajo.

Crecimiento de código JavaScript


El problema común de los sitios web modernos, no vinculados a una biblioteca específica, está relacionado con la cantidad de código JavaScript que un cliente generalmente tiene que cargar cuando navega. El Archivo HTTP ya tiene una buena cuenta de esto. En pocas palabras, así es como se ven los valores medios de los volúmenes de JavaScript descargados de sitios web en diferentes años:

  • 74.7 Kb - páginas web móviles, 15 de diciembre de 2011.
  • 384.4 Kb - páginas web móviles, 15 de diciembre de 2018.

Debe tenerse en cuenta que estos datos se obtuvieron después de procesar millones de páginas. Probablemente hay miles de sitios atípicos que distorsionan este indicador. Esta es una idea viable. Por lo tanto, intentaremos averiguar cómo se ve este indicador para los primeros 10,000 sitios del ranking de Alexa:

  • 381.5 Kb - páginas web móviles, 15 de diciembre de 2018 (aquí está la solicitud ).

Todo esto nos permite concluir que hoy en día se están creando sitios web que incluyen más código JS que los sitios que se crearon hace varios años. Esta es una observación importante. Los sitios se han vuelto más grandes, se han vuelto más interactivos y más complejos, y el volumen de código JS de estos sitios está creciendo gradualmente cada año. Probablemente ya haya escuchado sobre esto, pero cuanto más código JavaScript envíe al navegador, más tiempo llevará analizarlo, compilarlo y ejecutarlo. Esto, como resultado, ralentiza el sitio.

Es importante tener en cuenta que cada sitio es único, así como la base de usuarios de cada sitio. Muchos desarrolladores, cuyos sitios incluyen más de 300 KB de código JS, no enfrentan el hecho de que la mayoría de sus usuarios sufren problemas de rendimiento, y esto es completamente normal. Sin embargo, si le preocupa que pueda ser difícil para sus usuarios ver su sitio React, para evaluar la situación real, lo mejor es comenzar con la creación de perfiles.

Perfil y análisis de página


La creación de perfiles y el análisis de las aplicaciones React se pueden ver desde dos perspectivas:

  • En primer lugar, se trata de evaluar el rendimiento de los componentes. Esto afecta la forma en que los usuarios interactúan con el sitio. Por ejemplo, si se muestra una lista haciendo clic en un botón, esto debe hacerse rápidamente, pero si se vuelven a representar cientos de componentes durante esta operación, aunque no sea necesario, esta operación se percibirá como lenta.
  • En segundo lugar, estamos hablando de qué tan pronto la aplicación se pone en funcionamiento. Es decir, cuánto tiempo después del inicio de la carga del sitio, el usuario podrá interactuar con él. La cantidad de código enviado al usuario durante la carga de la primera página del sitio es un ejemplo de un factor que afecta la rapidez con que el usuario puede comenzar a trabajar con la aplicación.

Evaluación de desempeño y optimización de componentes


Tratemos de expresar en una oración el significado del algoritmo de reconciliación React, o la esencia de lo que se llama el "DOM virtual". Se verá así: "React toma medidas para distinguir entre el nuevo árbol DOM y el árbol viejo para comprender qué debe actualizarse exactamente en la interfaz de usuario cuando cambian los datos en el componente". Esto crea una carga mucho menor en el sistema que volver a representar la aplicación completa para cada cambio de estado o propiedades ( aquí puede leer sobre la diferencia entre O (n 3 ) y O (n)). Aquí hay un artículo de Dan Abramov, donde puede encontrar explicaciones sobre la reconciliación.

Incluso teniendo en cuenta el hecho de que estas optimizaciones están integradas en los mecanismos internos de React, siempre se puede encontrar un problema cuando los componentes de la aplicación se procesan repetidamente cuando esto no debería suceder. En aplicaciones pequeñas, esto puede no ser notable, pero puede afectar seriamente el rendimiento de las aplicaciones que muestran cientos de componentes en sus páginas.

La repetición innecesaria de componentes se realiza por muchas razones. Por ejemplo, las funciones que funcionan dentro de los componentes pueden no ser tan efectivas como podrían ser, o tal vez se vuelve a dibujar una lista completa de componentes cuando solo se agrega un nuevo elemento a esta lista. Hay herramientas que puede usar para averiguar qué árboles de componentes se han estado procesando durante demasiado tiempo. Entre ellos, se pueden observar los siguientes:

  • El Panel de herramientas de desarrollador de Chrome.
  • Reaccione el generador de herramientas Profiler.

▍ Análisis de rendimiento utilizando el panel de rendimiento de Chrome Developer Tools


React utiliza la API de sincronización del usuario para medir el tiempo necesario en cada paso del ciclo de vida del componente. La información de rendimiento para las aplicaciones React se puede recopilar y analizar utilizando las Herramientas para desarrolladores de Chrome. Esto le permite comprender la eficacia con la que los componentes están conectados, se muestran en la página y se desconectan durante la interacción del usuario con la página o cuando se recarga.


Análisis de rendimiento de componentes

Aquí hay un buen material sobre este tema, dedicado a investigar el rendimiento de las aplicaciones escritas usando React 16 usando las herramientas de desarrollador de Chrome.

La API de sincronización del usuario solo se usa durante el desarrollo. Es, en producción, está apagado. En tales condiciones, se pueden usar implementaciones más rápidas de tales mecanismos, sin tener un impacto serio en el rendimiento. Fue la necesidad de tales mecanismos lo que se convirtió en una de las razones para el desarrollo del nuevo API Profiler.

▍Análisis de rendimiento utilizando el generador de perfiles de las herramientas de desarrollador React


Con el lanzamiento de la biblioteca react-dom 16.5 en las herramientas de desarrollador React, se puede usar un nuevo panel llamado Profiler. Le permite analizar el rendimiento de la representación de componentes. Esto se realiza utilizando la API Profiler, que recopila información sobre el tiempo de ejecución de las operaciones para todos los componentes que se vuelven a representar.

El panel Profiler es una pestaña independiente en las herramientas de desarrollador React. Aquí, al igual que con el panel de rendimiento de la barra de herramientas de Chrome Developer Tools, puede registrar información sobre las acciones del usuario y las recargas de páginas para recopilar datos para analizar el rendimiento de los componentes.


Recopilación de datos con React Developer Tools

Una vez completada la recopilación de datos, se mostrará el llamado "gráfico ardiente", que muestra el tiempo requerido para representar los componentes en la página.


Horario del perfilador de llamas

Aquí puede cambiar entre diferentes confirmaciones o estados cuando se agregaron, eliminaron o actualizaron nodos DOM. Esto le permite obtener más información sobre cuánto tiempo se dedica a diversas operaciones con componentes.


Ver información de confirmación en el generador de perfiles

Las capturas de pantalla presentadas aquí representan los datos obtenidos como resultado de registrar las acciones del usuario realizadas en una aplicación simple. La aplicación descarga una lista de repositorios de tendencias de GitHub cuando se hace clic en un botón. Como puede ver, aquí solo hay dos confirmaciones:

  • Uno es para el indicador de carga, que se muestra durante la carga de la lista de elementos.
  • Otro representa el momento en que se completa la llamada a la API y la lista se muestra en el DOM.

La figura del lado derecho muestra metadatos útiles que incluyen información de confirmación o datos de componentes, como propiedades y estado.


Metadatos

Al trabajar con el generador de perfiles React, también puede ver otros datos representados por varios gráficos. Puede leer más sobre el generador de perfiles React en esta publicación del blog React.

Para complicar un poco nuestro ejemplo, considere una situación similar, pero ahora realizaremos muchas llamadas a la API para descargar repositorios de tendencias en diferentes lenguajes de programación (como Golang, JavaScript, etc.). Como es de esperar, con este enfoque, tenemos más compromisos a nuestra disposición.


Aumentar el número de confirmaciones al tiempo que complica el esquema de trabajo con la aplicación

Las confirmaciones posteriores se distinguen por horarios más largos, tienen más color amarillo. Esto significa que el tiempo requerido para que todos los componentes completen la representación aumenta a medida que aumenta el tamaño de la lista de elementos en la página. Esto se debe al hecho de que cada componente de la lista se vuelve a procesar con cada nueva llamada a la API. Esto ayuda a identificar un problema que puede resolverse con bastante facilidad. La solución es que los elementos de la lista no necesitan ser renderizados nuevamente cuando se agregan nuevos elementos a la lista.

▍ Minimizar las operaciones innecesarias de representación de componentes


Hay muchas maneras de deshacerse de las operaciones innecesarias para volver a representar los componentes React, o al menos para minimizar el número de tales operaciones. Consideremos algunos de ellos.

  • Puede usar el método del ciclo de vida del componente de shouldComponentUpdate () :

     shouldComponentUpdate(nextProps, nextState) { // true        } 
  • Puede usar PureComponent para construir componentes basados ​​en clases:

     import React, { PureComponent } from 'react'; class AvatarComponent extends PureComponent { } 
  • Para componentes funcionales, puede usar memo :

     import React, { memo } from 'react'; const AvatarComponent = memo(props => { }); 
  • Puede memorizar selectores de Redux (por ejemplo, usando reselect ).
  • Puede optimizar la salida de listas muy largas utilizando la virtualización (por ejemplo, utilizando react-window ).

Aquí y allá : un par de videos útiles que analizan el uso del perfilador React para encontrar cuellos de botella en las aplicaciones.

Evaluación de rendimiento y optimización de aplicaciones.


Además de analizar las mutaciones DOM y las operaciones de representación de componentes, hay otros fenómenos de nivel superior que vale la pena explorar. Para una evaluación exhaustiva del rendimiento del sitio, puede usar Lighthouse .

Hay tres formas de probar una página web con Lighthouse:

  • Uso de la interfaz de línea de comandos de Node.js.
  • Usando la extensión de Chrome.
  • Use la barra de herramientas Auditorías de las Herramientas para desarrolladores de Chrome.

Así es como se ve Lighthouse en el panel Auditorías.


Faro en el panel de Auditorías

Lighthouse generalmente no requiere mucho tiempo para recopilar todos los datos que necesita de la página y realizar muchas verificaciones de estos datos. Una vez completadas estas operaciones, Lighthouse muestra un informe con información resumida.

Para comprender que cargar una página en un navegador implica cargar demasiado código JavaScript y concluir que la cantidad de este código debe reducirse, debe prestar atención a las siguientes frases del informe:

  • Eliminar recursos de bloqueo de renderizado
  • El tiempo de arranque de JavaScript es demasiado alto
  • Evite cargas de red enormes

Si Lighthouse informa estos problemas porque la página usa un paquete JS demasiado grande, lo primero que debe considerar como una forma de solucionar el problema es dividir el paquete. El hecho es que si el código puede dividirse en fragmentos, algunos de los cuales son necesarios solo para trabajar con ciertas páginas del sitio, entonces no tenemos ninguna razón para no aprovechar esta oportunidad.

▍ Separación de paquetes JS


Una forma de dividir el código en partes es usar importaciones dinámicas:

   import('lodash.sortby')   .then(module => module.default)   .then(module => doSomethingCool(module)) 

La sintaxis de importación puede parecer una llamada de función, pero le permite importar cualquier módulo de forma asíncrona utilizando el mecanismo de promesas. En este ejemplo, el método sortby se importa primero de la biblioteca lodash , y luego se doSomethingCool() método doSomethingCool() .


Solicitud de clasificación de números

En este ejemplo, sucede lo siguiente:

  1. El usuario hace clic en un botón para ordenar los tres números.
  2. Importado lodash.sortby .
  3. Se doSomethingCool() método doSomethingCool() , que clasifica los números y los muestra en el nuevo nodo DOM.

Este es un ejemplo artificial extremadamente simple, porque si alguien necesita ordenar los números en una página web, entonces probablemente usará el método Array.prototype.sort() . , , .

, JavaScript TC39. Chrome Safari , Webpack , Rollup Parcel .
React, , . — React.lazy :

 import React, { lazy } from 'react'; const AvatarComponent = lazy(() => import('./AvatarComponent')); 

, , , . Suspense , , «» . React.lazy , , , :

 import React, { lazy, Suspense } from 'react'; import LoadingComponent from './LoadingComponent'; const AvatarComponent = lazy(() => import('./AvatarComponent')); const PageComponent = () => ( <Suspense fallback={LoadingComponent}>   <SomeComponent /> </Suspense> ) 

Suspense . React-, , , , React, loadable-components .

 import React from 'react'; import loadable from '@loadable/component' import LoadingComponent from './LoadingComponent'; const AvatarComponent = loadable(() => import('./AvatarComponent'), {   LoadingComponent: () => LoadingComponent }); 

LoadingComponent loadable-component .

, , loadable-components , - .

, . , , . React , React Suspense .

▍ , ?


, react-loadable-visibility , loadable-components -API Intersection Observer. , .

 import React from 'react'; import loadableVisibility from 'react-loadable-visibility/loadable-components' const AvatarComponent = loadableVisibility(() => import('./AvatarComponent'), { LoadingComponent: Loading, }) 

▍ ,


- — -, . - , , . , , , -, -, , .





Workbox
— , -, . CRA 2.0 , , src/index.js . - .

   import React from 'react'; //... //   ,          //    ,     // unregister()  register().    ,     - //    . //     : http://bit.ly/CRA-PWA serviceWorker.unregister(); 

- Workbox .

▍


- HTML-, , , . , , , , , JS-, .

, , DOM-, , , (, hydrate() React). , , , , .

React 16, . renderToString() HTML-, renderToNodeStream() Readable- Node.

HTML- , . , , .

React , , , renderToStaticNodeStream .

▍ ,


- , , , , - , , . , , .

, , , . HTML- , .

, react-snap , Puppeteer .

, .

▍ , CSS-in-JS


React-, , CSS-in-JS- emotion styled-components . , , , , , . CSS-in-JS , , , . , , JavaScript- , , . , , , .


, ( )

- , . emotion styled-components . Readable- Node. Glamor , .

, .


Preact-, ( )

, CSS-in-JS — , , , , , astroturf . , , , , .

▍


«», , . , , .

, Lighthouse. , React-, React A11y .

, react-axe , , JSX-.

▍


? -, , ? , ?

, - . «» . , — , .

create-react-app -:

 { "short_name": "React App", "name": "Create React App Sample", "icons": [   {     "src": "favicon.ico",     "sizes": "64x64 32x32 24x24 16x16",     "type": "image/x-icon"   } ], "start_url": ".", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" } 


, . , , . — . ¿Por qué es esto así? , , .

▍Atomic CSS


CSS- (Atomic CSS) , . , , :

 <button class="bg-blue">Click Me</button> 

padding :

 <button class="bg-blue pa2">Click Me</button> 

Y así sucesivamente. class DOM, , , CSS-, . , . Tachyons .

. tachyon-components API, API styled-components :

 import styled from 'tachyons-components' const Button = styled('button')` bg-blue pa2 ` <Button>Click Me</Button> 

, , CSS- «» , CSS-, , .

▍


, , . — :

 import { useState } from 'react'; function AvatarComponent() { const [name, setName] = useState('Houssein'); return (   <React.Fragment>     <div>       <p>This is a picture of {name}</p>       <img align="center" src="avatar.png" />     </div>     <button onClick={() => setName('a banana')}>       Fix name     </button>   </React.Fragment> ); } 

:


, . , , recompose .

React Conf, .

, , JavaScript- . , React , 1.5 ( ). , - , , , , , , , .

Resumen


, React-. , , :

  1. , :

    • Performance Chrome.
    • React.


    • , , shouldComponentUpdate() .
    • , , PureComponent .
    • React.memo .
    • Redux (, reselect ).
    • (, react-window ).
  2. Lighthouse.
  3. , :

    • — React.lazy .
    • — loadable-components .
    • - , . Workbox .
    • — ( renderToNodeStream renderToStaticNodeStream ).
    • ? react-snap .
    • , CSS-in-JS.
    • , . React A11y react-axe .
    • - , , , .

, React, , :

  • API, , .
  • , , , .

« » . -, .

Estimados lectores! React-?

Source: https://habr.com/ru/post/442650/


All Articles