Saltar al contenido principal
Versión: 0.82

Resumen de rendimiento

Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Una razón convincente para usar React Native en lugar de herramientas basadas en WebView es lograr al menos 60 fotogramas por segundo y proporcionar una apariencia y comportamiento nativos a tus aplicaciones. Siempre que es posible, React Native maneja las optimizaciones automáticamente, permitiéndote centrarte en tu aplicación sin preocuparte por el rendimiento. Sin embargo, hay áreas donde aún no hemos alcanzado ese nivel, y otras donde React Native (similar a escribir código nativo directamente) no puede determinar el mejor enfoque de optimización. En esos casos, se requiere intervención manual. Nos esforzamos por ofrecer una interfaz extremadamente fluida por defecto, pero puede haber situaciones donde eso no sea posible.

Esta guía te enseñará conceptos básicos para solucionar problemas de rendimiento, además de explicar fuentes comunes de problemas y sus soluciones sugeridas.

Lo que necesitas saber sobre los fotogramas

La generación de tus abuelos llamaba a las películas "imágenes en movimiento" por una razón: el movimiento realista en video es una ilusión creada al cambiar rápidamente imágenes estáticas a velocidad constante. Llamamos "fotogramas" a cada una de estas imágenes. La cantidad de fotogramas mostrados por segundo impacta directamente en qué tan fluida y finalmente realista parece una interfaz de usuario. Los dispositivos iOS y Android muestran al menos 60 fotogramas por segundo, lo que te da a ti y al sistema de UI como máximo 16.67ms para realizar todo el trabajo necesario para generar la imagen estática (fotograma) que el usuario verá en pantalla durante ese intervalo. Si no puedes completar el trabajo necesario dentro de ese tiempo, "se pierde un fotograma" y la interfaz parecerá no responder.

Para añadir un poco de complejidad, abre el Menú de desarrollo en tu aplicación y activa Show Perf Monitor. Notarás que hay dos tasas de fotogramas diferentes.

Captura del Monitor de Rendimiento

Tasa de fotogramas JS (hilo de JavaScript)

En la mayoría de aplicaciones React Native, tu lógica de negocio se ejecuta en el hilo de JavaScript. Aquí reside tu aplicación React, se hacen llamadas API, se procesan eventos táctiles y más. Las actualizaciones a vistas nativas se agrupan y envían al lado nativo al final de cada iteración del bucle de eventos, antes del plazo del fotograma (si todo va bien). Si el hilo JavaScript no responde durante un fotograma, se considera un fotograma perdido. Por ejemplo, si actualizas el estado del componente raíz en una aplicación compleja y esto provoca volver a renderizar subárboles de componentes computacionalmente costosos, podría tomar 200ms y perder 12 fotogramas. Las animaciones controladas por JavaScript parecerían congelarse durante ese tiempo. Si se pierden suficientes fotogramas, el usuario lo notará.

Un ejemplo es la respuesta a toques: si realizas trabajo durante múltiples fotogramas en el hilo JavaScript, podrías notar retrasos al interactuar con TouchableOpacity. Esto ocurre porque el hilo JavaScript está ocupado y no puede procesar los eventos táctiles enviados desde el hilo principal. Como resultado, TouchableOpacity no puede reaccionar a los toques ni ordenar a la vista nativa que ajuste su opacidad.

Tasa de fotogramas de UI (hilo principal)

Habrás notado que los navegadores de pila nativos (como @react-navigation/native-stack de React Navigation) tienen mejor rendimiento inicial que los basados en JavaScript. Esto se debe a que las transiciones animadas se ejecutan en el hilo principal nativo de UI, evitando interrupciones por pérdida de fotogramas en el hilo JavaScript.

De manera similar, puedes desplazarte sin problemas en un ScrollView cuando el hilo JavaScript está bloqueado, porque el ScrollView reside en el hilo principal. Los eventos de desplazamiento se envían al hilo JS, pero su recepción no es necesaria para que ocurra el desplazamiento.

Fuentes comunes de problemas de rendimiento

Ejecución en modo desarrollo (dev=true)

El rendimiento del hilo de JavaScript se ve muy afectado en modo desarrollo. Esto es inevitable: se requiere mucho más trabajo en tiempo de ejecución para proporcionar advertencias y mensajes de error útiles. Siempre verifica el rendimiento en compilaciones de producción.

Uso de declaraciones console.log

Al ejecutar una aplicación empaquetada, estas declaraciones pueden crear un cuello de botella en el hilo de JavaScript. Esto incluye llamadas de bibliotecas de depuración como redux-logger, así que asegúrate de eliminarlas antes del empaquetado. También puedes usar este plugin de Babel que elimina todas las llamadas console.*. Primero instálalo con npm i babel-plugin-transform-remove-console --save-dev, luego edita el archivo .babelrc en tu directorio de proyecto así:

json
{
"env": {
"production": {
"plugins": ["transform-remove-console"]
}
}
}

Esto eliminará automáticamente todas las llamadas console.* en las versiones de producción de tu proyecto.

Se recomienda usar el plugin incluso si no hay llamadas console.* en tu proyecto. Una biblioteca de terceros también podría hacerlas.

El renderizado de FlatList es lento o el rendimiento de desplazamiento es malo en listas grandes

Si tu FlatList se renderiza lentamente, asegúrate de haber implementado getItemLayout para optimizar la velocidad omitiendo la medición de los elementos renderizados.

También existen otras bibliotecas de listas de terceros optimizadas para rendimiento, como FlashList y Legend List.

Caída de FPS en el hilo JS por exceso de trabajo simultáneo

Las "transiciones lentas en navegadores" son la manifestación más común, pero puede ocurrir en otros casos. Usar InteractionManager es un buen enfoque, pero si el costo en experiencia de usuario es muy alto para retrasar trabajo durante una animación, considera LayoutAnimation.

La Animated API actualmente calcula cada fotograma clave bajo demanda en el hilo JavaScript a menos que configures useNativeDriver: true, mientras que LayoutAnimation aprovecha Core Animation y no se ve afectada por caídas de fotogramas en los hilos JS o principal.

Un caso de uso es animar un modal (deslizamiento desde arriba con fundido de superposición translúcida) mientras se inicializan y reciben respuestas de solicitudes de red, se renderiza el contenido del modal y se actualiza la vista desde donde se abrió. Consulta la guía de animaciones para más información sobre LayoutAnimation.

Advertencias:

  • LayoutAnimation solo funciona para animaciones de disparar-y-olvidar ("animaciones estáticas"). Si necesitas que sea interrumpible, usa Animated.

El movimiento de vistas (desplazamiento, traslación, rotación) reduce FPS en el hilo UI

Esto es especialmente notable en Android con texto sobre fondo transparente superpuesto a imágenes, o cualquier situación que requiera composición alfa para redibujar la vista en cada fotograma. Habilitar renderToHardwareTextureAndroid ayuda significativamente. En iOS, shouldRasterizeIOS ya está activado por defecto.

No abuses de esto o el uso de memoria se disparará. Perfila rendimiento y memoria al usar estas propiedades. Si ya no moverás una vista, desactiva esta propiedad.

Animación del tamaño de imágenes reduce FPS en el hilo UI

En iOS, cada vez que ajustas el ancho o la altura de un componente Image, la imagen original se recorta y escala nuevamente. Esto puede ser muy costoso en términos de rendimiento, especialmente para imágenes grandes. En su lugar, usa la propiedad de estilo transform: [{scale}] para animar el tamaño. Un ejemplo de cuándo podrías hacer esto es al tocar una imagen y ampliarla a pantalla completa.

Mi vista TouchableX no responde bien

A veces, si realizamos una acción en el mismo fotograma en que estamos ajustando la opacidad o el resaltado de un componente que responde a un toque, no veremos ese efecto hasta después de que la función onPress haya regresado. Esto puede ocurrir si onPress establece un estado que provoca una nueva renderización pesada y, como resultado, se pierden algunos fotogramas. Una solución es envolver cualquier acción dentro de tu manejador onPress en requestAnimationFrame:

tsx
function handleOnPress() {
requestAnimationFrame(() => {
this.doExpensiveAction();
});
}