En este codelab, mejorarás el rendimiento de la siguiente aplicación quitando las dependencias que no se usen ni sean necesarias.
Medir
Siempre es una buena idea medir primero el rendimiento de un sitio web antes de agregar optimizaciones.
- Para obtener una vista previa del sitio, presiona Ver app y, luego, Pantalla completa
.
Haz clic en tu gatito favorito. En esta aplicación, se usa la base de datos en tiempo real de Firebase, por lo que la puntuación se actualiza en tiempo real y se sincroniza con todas las demás personas que usan la aplicación. 🐈
- Presiona "Control + Mayúsculas + J" (o "Comando + Opción + J" en Mac) para abrir DevTools.
- Haga clic en la pestaña Red.
- Selecciona la casilla de verificación Inhabilitar la memoria caché.
- Vuelve a cargar la app.
Se envían casi 1 MB de JavaScript para cargar esta aplicación simple.
Consulta las advertencias del proyecto en Herramientas para desarrolladores.
- Haz clic en la pestaña Consola.
- Asegúrate de que
Warnings
esté habilitado en el menú desplegable de niveles junto a la entradaFilter
.
- Observa la advertencia que se muestra.
Firebase, que es una de las bibliotecas que se usan en esta aplicación, está siendo un buen samaritano al proporcionar una advertencia para que los desarrolladores sepan que no deben importar todo su paquete, sino solo los componentes que se usan. En otras palabras, hay bibliotecas sin usar que se pueden quitar de esta aplicación para que se cargue más rápido.
También hay casos en los que se usa una biblioteca en particular, pero puede haber una alternativa más simple. El concepto de quitar bibliotecas innecesarias se explora más adelante en este instructivo.
Analiza el paquete
La aplicación tiene dos dependencias principales:
- Firebase: Es una plataforma que proporciona varios servicios útiles para aplicaciones para iOS, Android o la Web. Aquí, se usa Realtime Database para almacenar y sincronizar la información de cada gatito en tiempo real.
- Moment.js: Una biblioteca de utilidades que facilita el manejo de fechas en JavaScript. La fecha de nacimiento de cada gatito se almacena en la base de datos de Firebase, y
moment
se usa para calcular su edad en semanas.
¿Cómo pueden dos dependencias contribuir a un tamaño de paquete de casi 1 MB? Bueno, una de las razones es que cualquier dependencia puede, a su vez, tener sus propias dependencias, por lo que hay muchas más que solo dos si se considera cada profundidad o rama del "árbol" de dependencias. Es fácil que una aplicación se vuelva grande con relativa rapidez si se incluyen muchas dependencias.
Analiza el bundler para tener una mejor idea de lo que sucede. Existen varias herramientas creadas por la comunidad que pueden ayudarte a hacerlo, como webpack-bundle-analyzer
.
El paquete de esta herramienta ya está incluido en la app como un devDependency
.
"devDependencies": {
//...
"webpack-bundle-analyzer": "^2.13.1"
},
Esto significa que se puede usar directamente en el archivo de configuración de webpack.
Importa al principio de webpack.config.js
:
const path = require("path");
//...
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
Ahora, agrégalo como un complemento al final del archivo dentro del array plugins
:
module.exports = {
//...
plugins: [
//...
new BundleAnalyzerPlugin()
]
};
Cuando se vuelva a cargar la aplicación, deberías ver una visualización de todo el paquete en lugar de la app en sí.
No es tan tierno como ver gatitos 🐱, pero es increíblemente útil. Si colocas el cursor sobre cualquiera de los paquetes, se mostrará su tamaño representado de tres maneras diferentes:
Tamaño de la estadística | Tamaño antes de cualquier minificación o compresión. |
---|---|
Tamaño analizado | Es el tamaño del paquete real dentro del paquete después de que se compiló. La versión 4 de webpack (que se usa en esta aplicación) minimiza los archivos compilados automáticamente, por lo que este tamaño es menor que el tamaño de las estadísticas. |
Tamaño comprimido con Gzip | Tamaño del paquete después de comprimirse con la codificación gzip. Este tema se trata en una guía aparte. |
Con la herramienta webpack-bundle-analyzer, es más fácil identificar los paquetes no utilizados o innecesarios que representan un gran porcentaje del paquete.
Cómo quitar paquetes que no se usan
La visualización muestra que el paquete firebase
consta de mucho más que solo una base de datos. Incluye paquetes adicionales, como los siguientes:
firestore
auth
storage
messaging
functions
Todos estos son servicios increíbles que proporciona Firebase (y puedes consultar la documentación para obtener más información), pero ninguno de ellos se usa en la aplicación, por lo que no hay motivo para importarlos todos.
Revierte los cambios en webpack.config.js
para volver a ver la aplicación:
- Quita
BundleAnalyzerPlugin
de la lista de complementos:
plugins: [
//...
new BundleAnalyzerPlugin()
];
- Ahora quita la importación sin usar de la parte superior del archivo:
const path = require("path");
//...
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
Ahora la aplicación debería cargarse normalmente. Modifica src/index.js
para actualizar las importaciones de Firebase.
import firebase from 'firebase';
import firebase from 'firebase/app';
import 'firebase/database';
Ahora, cuando se vuelva a cargar la app, no se mostrará la advertencia de Herramientas para desarrolladores. Abrir el panel Red de DevTools también muestra una buena reducción en el tamaño del paquete:
Se quitó más de la mitad del tamaño del paquete. Firebase proporciona muchos servicios diferentes y les da a los desarrolladores la opción de incluir solo aquellos que realmente se necesitan. En esta aplicación, solo se usó firebase/database
para almacenar y sincronizar todos los datos. Siempre se requiere la importación de firebase/app
, que configura la superficie de la API para cada uno de los diferentes servicios.
Muchas otras bibliotecas populares, como lodash
, también permiten a los desarrolladores importar de forma selectiva diferentes partes de sus paquetes. Sin mucho trabajo, actualizar las importaciones de bibliotecas en una aplicación para incluir solo lo que se usa puede generar mejoras significativas en el rendimiento.
Si bien el tamaño del paquete se redujo bastante, aún queda trabajo por hacer. 😈
Cómo quitar paquetes innecesarios
A diferencia de Firebase, importar partes de la biblioteca moment
no es tan sencillo, pero ¿quizás se pueda quitar por completo?
El cumpleaños de cada gatito tierno se almacena en formato Unix (milisegundos) en la base de datos de Firebase.
Es la marca de tiempo de una fecha y hora específicas representadas por la cantidad de milisegundos transcurridos desde el 1 de enero de 1970 a las 00:00 (UTC). Si la fecha y hora actuales se pueden calcular en el mismo formato, probablemente se pueda construir una pequeña función para encontrar la edad de cada gatito en semanas.
Como siempre, intenta no copiar y pegar mientras sigues los pasos. Comienza por quitar moment
de las importaciones en src/index.js
.
import firebase from 'firebase/app';
import 'firebase/database';
import * as moment from 'moment';
Hay un objeto de escucha de eventos de Firebase que controla los cambios de valores en nuestra base de datos:
favoritesRef.on("value", (snapshot) => { ... })
Sobre este, agrega una pequeña función para calcular la cantidad de semanas a partir de una fecha determinada:
const ageInWeeks = birthDate => {
const WEEK_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 7;
const diff = Math.abs((new Date).getTime() - birthDate);
return Math.floor(diff / WEEK_IN_MILLISECONDS);
}
En esta función, se calcula la diferencia en milisegundos entre la fecha y hora actuales (new Date).getTime()
y la fecha de nacimiento (el argumento birthDate
, ya en milisegundos) y se divide por la cantidad de milisegundos en una sola semana.
Por último, se pueden quitar todas las instancias de moment
en el objeto de escucha de eventos aprovechando esta función:
favoritesRef.on("value", (snapshot) => { const { kitties, favorites, names, birthDates } = snapshot.val(); favoritesScores = favorites; kittiesList.innerHTML = kitties.map((kittiePic, index) => {const birthday = moment(birthDates[index]);return ` <li> <img src=${kittiePic} onclick="favKittie(${index})"> <div class="extra"> <div class="details"> <p class="name">${names[index]}</p><p class="age">${moment().diff(birthday, 'weeks')} weeks old</p><p class="age">${ageInWeeks(birthDates[index])} weeks old</p> </div> <p class="score">${favorites[index]} ❤</p> </div> </li> `}) });
Ahora, vuelve a cargar la aplicación y observa el panel Network una vez más.
El tamaño de nuestro paquete se redujo en más de la mitad nuevamente.
Conclusión
Con este codelab, deberías comprender bastante bien cómo analizar un paquete en particular y por qué puede ser tan útil quitar los paquetes que no se usan o que no son necesarios. Antes de comenzar a optimizar una aplicación con esta técnica, es importante saber que puede ser mucho más compleja en aplicaciones más grandes.
En cuanto a quitar las bibliotecas que no se usan, intenta averiguar qué partes de un paquete se usan y cuáles no. En el caso de un paquete de aspecto misterioso que parece no usarse en ningún lugar, retrocede y verifica qué dependencias de nivel superior podrían necesitarlo. Intenta encontrar una forma de desacoplarlos.
Cuando se trata de quitar bibliotecas innecesarias, las cosas pueden ser un poco más complicadas. Es importante trabajar en estrecha colaboración con tu equipo y ver si existe la posibilidad de simplificar partes de la base de código. Quitar moment
en esta aplicación puede parecer lo correcto en cada ocasión, pero ¿qué sucedería si hubiera zonas horarias y diferentes configuraciones regionales que se debieran controlar? ¿O qué sucedería si hubiera manipulaciones de fechas más complicadas? Las cosas pueden complicarse mucho cuando se manipulan y analizan fechas y horas, y las bibliotecas como moment
y date-fns
simplifican esto de manera significativa.
Todo es un intercambio, y es importante evaluar si vale la pena la complejidad y el esfuerzo de implementar una solución personalizada en lugar de depender de una biblioteca de terceros.