Saltar al contenido principal




Un estudio de caso del mundo real sobre la optimización del rendimiento de React SPA.

El rendimiento del sitio web no se trata solo del tiempo de carga. Es fundamental proporcionar una experiencia rápida y receptiva a los usuarios, especialmente para las aplicaciones de escritorio de productividad que la gente usa todos los días. El equipo de ingenieros de Tecnologías de reclutamiento pasó por un proyecto de refactorización para mejorar una de sus aplicaciones web, AirSHIFT, para un mejor rendimiento de entrada del usuario. Así es como lo hicieron.

Respuesta lenta, menor productividad

AirSHIFT es una aplicación web de escritorio que ayuda a los propietarios de tiendas, como restaurantes y cafés, a gestionar el trabajo por turnos de los miembros de su personal. Construida con React, la aplicación de una sola página proporciona funciones de cliente enriquecidas que incluyen varias tablas de cuadrícula de horarios de turnos organizados por día, semana, mes y más.

airshift_visual-3001408

A medida que el equipo de ingeniería de Recruit Technologies agregó nuevas funciones a la aplicación AirSHIFT, comenzaron a recibir más comentarios sobre el rendimiento lento. El gerente de ingeniería de AirSHIFT, Yosuke Furukawa, dijo:

En un estudio de investigación de usuarios, nos sorprendió cuando uno de los dueños de la tienda dijo que dejaría su asiento para preparar café después de hacer clic en un botón, solo para matar el tiempo esperando que se cargara la mesa de turnos.

Después de realizar la investigación, el equipo de ingeniería se dio cuenta de que muchos de sus usuarios intentaban cargar tablas de turnos masivas en computadoras de baja especificación, como una computadora portátil Celeron M de 1 GHz de hace 10 años.

Spinner sin fin en dispositivos de gama baja.

La aplicación AirSHIFT estaba bloqueando el hilo principal con scripts costosos, pero el equipo de ingeniería no se dio cuenta de lo costosos que eran los scripts porque se estaban desarrollando y probando en computadoras de especificaciones ricas con conexiones Wi-Fi rápidas.

main-thread-break-down-7210124

Al cargar la tabla de turnos, alrededor del 80% del tiempo de carga se consumió ejecutando scripts.

Después de perfilar su rendimiento en Chrome DevTools con la limitación de CPU y red habilitada, quedó claro que se necesitaba optimizar el rendimiento. AirSHIFT formó un grupo de trabajo para abordar este problema. Aquí hay 5 cosas en las que se centraron para hacer que su aplicación responda mejor a la entrada del usuario.

1. Virtualizar tablas grandes

Mostrar la tabla de turnos requirió varios pasos costosos: construir el DOM virtual y representarlo en la pantalla en proporción al número de miembros del personal y franjas horarias. Por ejemplo, si un restaurante tiene 50 miembros que trabajan y quiere verificar su horario de turnos mensual, sería una tabla de 50 (miembros) multiplicada por 30 (días), lo que daría lugar a 1.500 componentes de celda para renderizar. Esta es una operación muy cara, especialmente para dispositivos de baja especificación. En realidad, las cosas fueron peor. A partir de la investigación, descubrieron que había tiendas que administraban a 200 miembros del personal, lo que requería alrededor de 6,000 componentes de celda en una sola tabla mensual.

Para reducir el costo de esta operación, AirSHIFT virtualizó la tabla de turnos. La aplicación ahora solo monta los componentes dentro de la ventana gráfica y desmonta los componentes fuera de la pantalla.

virtualize_before-9209523

Antes: renderizando todas las celdas de la tabla de turnos.

virtualize_after-8560709

Después: solo renderiza las celdas dentro de la ventana gráfica.

En este caso, AirSHIFT utilizó reaccionar virtualizado ya que existían requisitos en torno a la habilitación de tablas de cuadrícula bidimensionales complejas. También están explorando formas de convertir la implementación para usar la ventana de reacción ligera en el futuro.

Resultados

La virtualización de la tabla por sí sola redujo el tiempo de creación de scripts en 6 segundos (en un entorno de Macbook Pro 4x más lento + Fast 3G acelerado). Esta fue la mejora de rendimiento más impactante en el proyecto de refactorización.

virtualize_results_before-4813935

Antes: alrededor de 10 segundos de secuencia de comandos después de la entrada del usuario.

virtualize_results_after-6955380

Después: 4 segundos de secuencia de comandos después de la entrada del usuario.

2. Auditoría con API User Timing

A continuación, el equipo de AirSHIFT refactorizó los scripts que se ejecutan con la entrada del usuario. los tabla de llamas
de DevTools de Chrome
permite analizar lo que está sucediendo realmente en el hilo principal. Pero al equipo de AirSHIFT le resultó más fácil analizar la actividad de las aplicaciones en función del ciclo de vida de React.

React 16 proporciona su seguimiento de rendimiento a través del
API de tiempo de usuario, que puede visualizar desde el
Sección de tiempos
de Chrome DevTools. AirSHIFT usó la sección Tiempos para encontrar lógica innecesaria que se ejecuta en eventos del ciclo de vida de React.

user_timing-6925370

Eventos de tiempo de usuario de React.

Resultados

El equipo de AirSHIFT descubrió que un
Reconciliación del árbol de reacción
estaba sucediendo justo antes de cada ruta de navegación. Esto significaba que React estaba actualizando la tabla de turnos innecesariamente antes de las navegaciones. Una actualización innecesaria del estado de Redux estaba causando este problema. Arreglarlo ahorró alrededor de 750 ms de tiempo de scripting. AirSHIFT también realizó otras microoptimizaciones que eventualmente llevaron a una reducción total de 1 segundo en el tiempo de programación.

3. Componentes de carga diferida y transferencia de lógica costosa a los trabajadores web

AirSHIFT tiene una aplicación de chat incorporada. Muchos propietarios de tiendas se comunican con los miembros de su personal a través del chat mientras miran la tabla de turnos, lo que significa que un usuario puede estar escribiendo un mensaje mientras se carga la tabla. Si el hilo principal está ocupado con scripts que están renderizando la tabla, la entrada del usuario podría ser fastidiosa.

Para mejorar esta experiencia, AirSHIFT ahora usa React.lazy y Suspense para mostrar marcadores de posición para el contenido de la tabla mientras carga lentamente los componentes reales.

El equipo de AirSHIFT también migró parte de la costosa lógica empresarial dentro de los componentes cargados con pereza a
trabajadores web. Esto resolvió el problema del jank de entrada del usuario al liberar el hilo principal para que pudiera enfocarse en responder a la entrada del usuario.

Normalmente, los desarrolladores se enfrentan a la complejidad al utilizar trabajadores, pero esta vez Comlink hizo el trabajo pesado por ellos. A continuación se muestra el pseudocódigo de cómo AirSHIFT trabajó en una de las operaciones más costosas que tuvieron: calcular los costos laborales totales.

En App.js, use React.lazy y Suspense para mostrar contenido de respaldo mientras se carga


import React, { lazy, Suspense } from 'react'


const Hello = lazy(() => import('./Cost'))

const Loading = () => (
<div>Some fallback content to show while loading</div>
)


export default function App({ userInfo }) {
return (
<div>
<Suspense fallback={<Loading />}>
<Cost />
</Suspense>
</div>
)
}

En el componente Costo, use comlink para ejecutar la lógica de cálculo


import React from 'react';
import { proxy } from 'comlink';


const WorkerlizedCostCalc = proxy(new Worker('./WorkerlizedCostCalc.js'));
export default function Cost({ userInfo }) {
const instance = await new WorkerlizedCostCalc();
const cost = await instance.calc(userInfo);
return <p>{cost}</p>;
}

Implementar la lógica de cálculo que se ejecuta en el trabajador y exponerla con comlink


import { expose } from 'comlink'
import { someExpensiveCalculation } from './CostCalc.js'


expose({
calc(userInfo) {
return someExpensiveCalculation(userInfo);
}
}, self);

Resultados

A pesar de la cantidad limitada de lógica que trabajaron como prueba, AirSHIFT cambió alrededor de 100 ms de su JavaScript del hilo principal al hilo de trabajo (simulado con aceleración de CPU 4x).

worker-7404546

AirSHIFT está explorando actualmente si pueden cargar otros componentes de forma diferida y descargar más lógica a los trabajadores web para reducir aún más el jank.

4. Establecer un presupuesto de desempeño

Una vez implementadas todas estas optimizaciones, era fundamental asegurarse de que la aplicación siga funcionando con el tiempo. AirSHIFT ahora usa tamaño de paquete no exceder el tamaño de archivo JavaScript y CSS actual. Además de establecer estos presupuestos básicos, crearon un tablero para mostrar varios percentiles del tiempo de carga de la tabla de turnos para verificar si la aplicación funciona incluso en condiciones no ideales.

  • Ahora se mide el tiempo de finalización del script para cada evento de Redux
  • Los datos de rendimiento se recopilan en Elasticsearch
  • El desempeño de los percentiles 10, 25, 50 y 75 de cada evento se visualiza con Kibana

AirSHIFT ahora está monitoreando el evento de carga de la tabla de turnos para asegurarse de que se complete en 3 segundos para los usuarios del percentil 75. Este es un presupuesto no aplicado por ahora, pero están considerando las notificaciones automáticas a través de Elasticsearch cuando exceden su presupuesto.

kibana-9058952

El panel de Kibana que muestra datos de rendimiento diario por percentiles.

Resultados

En el gráfico anterior, puede ver que AirSHIFT ahora está alcanzando principalmente el presupuesto de 3 segundos para los usuarios del percentil 75 y también carga la tabla de turnos en un segundo para los usuarios del percentil 25. Al capturar datos de rendimiento de RUM de diversas condiciones y dispositivos, AirSHIFT ahora puede verificar si una nueva versión de función está afectando realmente el rendimiento de la aplicación o no.

5. Hackatones de rendimiento

Aunque todos estos esfuerzos de optimización del rendimiento fueron importantes e impactantes, no siempre es fácil lograr que los equipos de ingeniería y de negocios prioricen el desarrollo no funcional. Parte del desafío es que algunas de estas optimizaciones de rendimiento no se pueden planificar. Requieren experimentación y una mentalidad de prueba y error.

AirSHIFT ahora está llevando a cabo hackatones de rendimiento internos de 1 día para permitir que los ingenieros se concentren solo en el trabajo relacionado con el rendimiento. En estos hackatones eliminan todas las limitaciones y respetan la creatividad de los ingenieros, lo que significa que vale la pena considerar cualquier implementación que contribuya a la velocidad. Para acelerar el hackathon, AirSHIFT divide el grupo en equipos pequeños y cada equipo compite para ver quién puede obtener el mayor Faro mejora de la puntuación de rendimiento. ¡Los equipos se vuelven muy competitivos! 🔥

hackathon-2457180

Resultados

El enfoque del hackathon les está funcionando bien.

  • Los cuellos de botella en el rendimiento se pueden detectar fácilmente probando múltiples enfoques durante el hackathon y midiendo cada uno con Lighthouse.
  • Después del hackathon, es bastante fácil convencer al equipo de qué optimización deben priorizar para el lanzamiento de producción.
  • También es una forma eficaz de defender la importancia de la velocidad. Todos los participantes pueden comprender la correlación entre cómo codifica y cómo se traduce en rendimiento.

Un buen efecto secundario fue que muchos otros equipos de ingeniería dentro de Recruit se interesaron en este enfoque práctico y el equipo de AirSHIFT ahora está facilitando múltiples hackatones de velocidad dentro de la empresa.

Resumen

Definitivamente no fue el viaje más fácil para AirSHIFT trabajar en estas optimizaciones, pero ciertamente valió la pena. Ahora AirSHIFT está cargando la tabla de turnos en 1,5 segundos en la mediana, lo que representa una mejora 6 veces superior a su rendimiento antes del proyecto.


Después de que se lanzaron las optimizaciones de rendimiento, un usuario dijo:

Muchas gracias por acelerar la carga de la mesa de turnos. Organizar el trabajo por turnos es mucho más eficiente ahora.

R Marketing Digital