6 consejos de optimización para aplicaciones React

###IntroducciónEn los últimos años, los frameworks de JavaScript han cambiado por completo la forma en que creamos aplicaciones, y React ha tenido su parte en el proceso de conversión. Optimizar el tiempo de carga de la página es importante porque el tiempo que tarda una página en cargarse se correlaciona directamente con las tasas de rebote y conversión. En este tutorial, analizaremos seis errores comunes que cometen la mayoría de los desarrolladores al crear aplicaciones con React. También analizaremos cómo se pueden evitar estos errores y destacaremos consejos útiles para mantener el tiempo de carga de la página lo más bajo posible.
Cómo funciona React
La imagen de arriba explica lo que sucede cada vez que usas una aplicación React. Cada aplicación React comienza con un componente raíz, que en nuestro caso es App
, y está compuesta por componentes "secundarios": AddGrocery
, GroceryList
y SearchBar
. Estos componentes secundarios consisten en funciones que representan la interfaz de usuario en el DOM en función de las propiedades y el estado que contienen.
Los usuarios interactúan con la interfaz de usuario renderizada de diferentes maneras: completando un formulario o haciendo clic en un botón. Cuando esto sucede, los eventos se transmiten al componente principal. Estos eventos provocan un cambio en el estado de la aplicación, lo que hace que React vuelva a renderizar la interfaz de usuario en su DOM virtual.
Por cada evento que se envía de vuelta, el motor de React tiene que comparar el DOM virtual con el DOM real y calcular si es necesario actualizar el DOM real con estos nuevos datos encontrados. Ahora, donde las cosas se complican es si nuestra aplicación está estructurada de manera que cada clic y desplazamiento da como resultado que React compare y actualice repetidamente los cambios entre el DOM virtual y el real. Esto podría dar como resultado una aplicación terriblemente lenta e ineficiente.
Errores comunes y cómo evitarlos
A continuación, se presentan tres malas prácticas que cometen los desarrolladores al crear aplicaciones. Estos errores reducen la eficiencia y aumentan los tiempos de carga de las páginas en general. Evítelos tanto como pueda.
Importaciones innecesarias
Siempre que importas una biblioteca completa en tu aplicación, se desestructura para acceder al módulo que necesitas. Una o dos bibliotecas pequeñas pueden no hacer daño, pero cuando importas muchas bibliotecas y dependencias, debes importar solo el módulo que necesitas:
import assign from "101";import Map from "immutable";
El código anterior importa toda la biblioteca y comienza a desestructurarla para acceder a ella assign
y a Map
. En lugar de hacer eso, usemos un concepto llamado "selección selectiva" que solo toma las partes necesarias de lo que necesita su aplicación:
import assign from "101/assign";import Map from "immutable/src/map";
Funciones de inserción en JSX
JavaScript es conocido por ser un lenguaje que recolecta elementos no utilizados. Su recolector de elementos no utilizados administra la memoria intentando recuperar la memoria que ya no utilizan las partes de la aplicación. Definir una función en render creará una nueva instancia de la función cada vez que se vuelva a renderizar el componente que la contiene. Cuando esta práctica ocurre en toda la aplicación, eventualmente se convierte en un problema para el recolector de elementos no utilizados en forma de fugas de memoria, una situación en la que la memoria utilizada por partes de la aplicación no se libera aunque esas partes ya no la utilicen:
class GroceryList extends React.Component { state = { groceries: [], selectedGroceryId: null } render(){ const { groceries } = this.state; return ( groceries.map((grocery)={ return Grocery onClick={(e)={ this.setState({selectedGroceryId:grocery.groceryId}) }} grocery={grocery} key={grocery.id}/ }) ) }}
Lo correcto sería definir una nueva función onGroceryClick
justo antes render
:
class GroceryList extends React.Component { state = { groceries: [], selectedGroceryId: null } onGroceryClick = (groceryId)={ this.setState({selectedGroceryId:groceryId}) } render(){ const { groceries } = this.state; return ( groceries.map((grocery)={ return Grocery onClick={this.onGroceryClick} grocery={grocery} key={grocery.id}/ }) ) }}
Uso del operador Spread en elementos DOM
Al crear aplicaciones, es una mala práctica utilizar el operador spread sin reservas. Esto hará que aparezcan atributos desconocidos y es solo cuestión de tiempo antes de que las cosas comiencen a complicarse. A continuación, se incluye un breve ejemplo:
const GroceriesLabel = props = { return ( div {...props} {props.text} /div ); };
A medida que la aplicación que estás creando se hace más grande, . . .props
puede contener cualquier cosa. Una práctica mejor y más segura es identificar los valores que necesitas:
const GroceriesLabel = props = { return ( div particularValue={props.particularValue} {props.text} /div );};
Trucos para mejorar el rendimiento
Utilice Gzip para comprimir sus paquetes
Esto es muy eficiente y puede reducir el tamaño de sus archivos hasta en un 65%. La mayoría de los archivos de su aplicación utilizarán mucho texto repetido y espacios en blanco. Gzip se encarga de esto comprimiendo estas cadenas recurrentes, acortando así drásticamente el tiempo de primera renderización de su sitio web. Gzip comprime previamente sus archivos utilizando compressionPlugin
el complemento local de Webpack para comprimir archivos durante la producción. Primero, creemos un paquete comprimido utilizando compressionPlugin
:
plugins: [ new CompressionPlugin({ asset: "[path].gz[query]", algorithm: "gzip", test: /.js$|.css$|.html$/, threshold: 10240, minRatio: 0.8 })]
Una vez comprimido el archivo, se puede servir mediante middleware:
//this middleware serves all js files as gzipapp.use(function(req, res, next) { const originalPath = req.path; if (!originalPath.endsWith(".js")) { next(); return; } try { const stats = fs.statSync(path.join("public", `${req.path}.gz`)); res.append('Content-Encoding', 'gzip'); res.setHeader('Vary', 'Accept-Encoding'); res.setHeader('Cache-Control', 'public, max-age=512000'); req.url = `${req.url}.gz`; const type = mime.lookup(path.join("public", originalPath)); if (typeof type != 'undefined') { const charset = mime.charsets.lookup(type); res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : '')); } } catch (e) {} next();})
Memorización de componentes de React
El concepto de memorización no es nuevo: es una técnica que almacena llamadas de funciones costosas y devuelve el resultado almacenado en caché cuando se produce nuevamente la misma entrada. La memorización en React funciona memorizando el cálculo de datos para que los cambios de estado se produzcan lo más rápido posible. Para comprender cómo funciona esto, observe el componente de React a continuación:
const GroceryDetails = ({grocery, onEdit}) = { const {name, price, grocery_img} = grocery; return ( div className="grocery-detail-wrapper" img src={grocery_img} / h3{name}/h3 p{price}/p /div )}
En el ejemplo de código anterior, todos los elementos secundarios GroceryDetails
se basan en propiedades. Si se cambian las propiedades, se volverá GroceryDetails
a renderizar. Si GroceryDetails es un componente que es poco probable que cambie, se debe memorizar. Los usuarios de versiones anteriores de React (V16.6.0) usarían moize
, una biblioteca de memorización para JavaScript. Observa la sintaxis a continuación:
import moize from 'moize/flow-typed';const GroceryDetails = ({grocery, onEdit}) ={ const {name, price, grocery_img} = grocery; return ( div className="grocery-detail-wrapper" img src={grocery_img} / h3{name}/h3 p{price}/p /div )}export default moize(GroceryDetails,{ isReact: true});
En el bloque de código anterior, moize
se almacenan los elementos y el contexto pasados GroceryDetails
y se utilizan ambos para detectar si hay actualizaciones en el componente. Mientras los elementos y el contexto permanezcan sin cambios, se devolverán los valores almacenados en caché. Esto garantiza una representación superrápida.
Para los usuarios que trabajan con versiones posteriores de React (superiores a V16.6.0), pueden usar React.memo
en lugar de moize:
const GroceryDetails = ({grocery, onEdit}) ={ const {name, price, grocery_img} = grocery; return ( div className="grocery-detail-wrapper" img src={grocery_img} / h3{name}/h3 p{price}/p /div )}export default React.memo(GroceryDetails);
Considere utilizar la representación del lado del servidor
La representación del lado del servidor (SSR) es la capacidad de un marco front-end de representar el marcado mientras se ejecuta en un sistema back-end.
Deberías aprovechar SSR con aplicaciones de página única. En lugar de que los usuarios esperen a que se carguen tus archivos JavaScript, los usuarios de tu aplicación reciben una página HTML completamente renderizada tan pronto como la solicitud inicial enviada devuelve una respuesta. Generalmente, las aplicaciones renderizadas del lado del servidor permiten que los usuarios reciban contenido mucho más rápido que las aplicaciones renderizadas del lado del cliente. Algunas soluciones para la renderización del lado del servidor en React incluyen Next.js y Gatsby.
Conclusión
Para el principiante promedio que puede no estar familiarizado con estos conceptos, le sugiero que consulte este curso sobre cómo comenzar con React, ya que cubre un montón de temas, desde el manejo del estado hasta la creación de componentes. La importancia de optimizar el tiempo de carga de la página en su aplicación React no se puede subestimar. Su aplicación no solo facilitará un mejor SEO y generará una mayor tasa de conversión, sino que al seguir las mejores prácticas podrá detectar errores más fácilmente. Estos conceptos pueden requerir mucho esfuerzo, pero vale la pena intentarlos. Publicaciones como Memoize React Components de Tony Quetano y How to serve a Webpack gzipped file in production de Selva Ganesh ayudaron a escribir esto.
Deja una respuesta