Ejemplo de migración Firebase.

Para crear una aplicación web, es necesario optimizar y disminuir al máximo el tamaño de nuestro proyecto con el objetivo de dar una respuesta rápida a nuestros usuarios. Dentro de esta idea, un punto importante a optimizar es la librería Firebase ya que sus 1,5MB pueden ser un problema importante en el rendimiento de nuestro proyecto.

Intentando dar una solución al problema, he encontrado una versión, exactamente la 9, dónde la librería disminuye un 50 por ciento el tamaño en ejecución. En este artículo explicaremos cómo realizar esta migración y os mostraremos ejemplos reales de esta conversión.

Aparte, se ha dejado a disposición de los usuarios un código de ejemplo en la siguiente URL: https://github.com/al118345/migracion_angular_firebase9

1. Optimización de tu proyecto angular

Cuando se configura un nuevo proyecto angular, es necesario instalar todos los paquetes npm necesarios para ejecutar el proyecto y, a partir de este punto, comienzas a trabajar en la implementación de tu proyecto. Tarde o temprano te encuentras algún problema que requiere una codificación especial, por lo que recurres a Google y busca una respuesta, probablemente un paquete. Lo instalas y compilas el proyecto. Luego miras su tamaño y ... así es como te da un infarto. ¿Cómo terminé con 15 MB node_modules?

Al contrario de lo que puede pensar un programador primerizo, el tamaño de la aplicación es importante por lo siguiente:

  • Rendimiento: una aplicación que es liviana se ejecuta más rápido, consume menos recursos y funciona de manera más eficiente. Esto es importante ya que, más del 40% de usuarios abandonan la web si esta tarda más de 3 segundos en cargar.
  • Costes: las aplicaciones ligeras requieren menos recursos, lo que significa que el coste de infraestructura disminuye y, por lo tanto, existe un ahorro de presupuesto.

2. Adelgazar la aplicación

Es cierto, y yo soy un ejemplo, que a los desarrolladores nos gusta simplificar nuestro trabajo y, en muchas ocasiones, esto supone importar bibliotecas externas a nuestros proyectos. Desafortunadamente, realizar estas operativas supone aumentar el tamaño de nuestra web Angular y, en las ocasiones más radicales, puede salirse de control.

¿Cómo solucionarlo? Actualmente, la mejor forma de conseguir disminuir el tamaño de tu aplicación Angular es a través de la librería webpack-bundle-analyzer. Este plugin permite analizar qué dependencias componen nuestros bundles al detalle.

Esta aplicación despliega una gráfica interactiva con el resultado de nuestro bundle, qué dependencias contiene , cuánto pesan y cómo se distribuyen.

Dicho esto, para instalar la libreria aplica el siguiente comando:

npm install --save-dev webpack-bundle-analyzer

Una vez instalado, en el momento que compilamos el proyecto, tenemos que añadir la siguiente opción --stats-json de forma que quede algo tal que así:

ng build  --configuration production --aot --vendor-chunk --common-chunk --delete-output-path --build-optimizer  --stats-json

Automáticamente se genera un fichero stats.json.

El siguiente paso seria ejecutar el siguiente comando, dónde path_to_stats_file es la ruta al fichero.

webpack-bundle-analyzer path_to_stats_file/stats.json

El resultado es el siguiente mapa de calor:

1 Ejemplo resultado

3. Ejemplo con Firebase.

Las aplicaciones que actualmente usan Firebase Web SDK versión 8 o anterior tiene una cosa en común, un fichero node_modules de tamaño desmesurado como el que se puede observar a continuación:

1 Ejemplo tamaño firebase versión 7

Es decir, el rendimiento y la eficiencia de vuestra aplicación web se verá mermado por la utilización de está librería tan pesada. Para evitar estos problemas, los desarrolladores de google han implementado una nueva versión (9) muy adaptada a entornos dónde la eficiencia y el tamaño reducido son necesarios. Dicho esto, en este punto vamos a exponer qué procedimientos hay que llevar a cabo para realizar una migración a esta nueva versión.

Instalación de la librería.

Para instalar la librería aplica el siguiente comando:
npm i firebase@exp
Una vez realizado, elimina de src/app/app.module.ts los siguientes módulos
    AngularFireDatabaseModule,
    DeviceDetectorModule.forRoot(),
    AngularFireModule.initializeApp(environment.firebase),
    AngularFirestoreModule,
    AngularFireAuthModule

Un pequeño detalle, en este ejemplo he almacenado la credenciales de acceso al entorno firebase en el fichero src/environments/environments.prod.ts con la siguiente estructura:

 export const environment = {
  production: false,
  firebase: {
    apiKey: '',
    authDomain: '',
    databaseURL: '',
    projectId: '',
    storageBucket: '',
    messagingSenderId: '',
    appId: '',
    measurementId: ''
  }
};

Una vez se han eliminado los módulos, directamente he importado las siguientes librerías para el ejemplo. Estas librerías se importan directamente sobre el componente o el servicio que los necesite, no el app.module.ts


import { Injectable } from '@angular/core';
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";

import { getFirestore, collection, query, where, getDocs } from "firebase/firestore";
import { getDatabase, ref, child, get } from "firebase/database";

import {environment} from "../environments/environment";
import { initializeApp } from "firebase/app"

Una vez realizada la modificación, vamos a poner algunos ejemplos. Empezaremos con el ejemplo en login en firebase 7 utilizando un usuario y contraseña:

  doLogin() {
    return new Promise<any>((resolve, reject) => {
      firebase.auth().signInWithEmailAndPassword('correo', 'Pass')
        .then(res => {
          resolve(res);
        }, err => reject(err))
    })
  }
Su correspondiente en la versión 9 es:

doLogin(): Promise<Boolean> {
    const firebaseApp = initializeApp(environment.firebase);
    const auth = getAuth(firebaseApp);
    return signInWithEmailAndPassword(auth, 'correo', 'pass')
      .then((userCredential) => {
        // Signed in
        return true
      })
      .catch((error) => {
        return false
      });
 }
En otro sentido, si queremos obtener un listado de elementos de una base de datos realtime firebase deberíamos aplicar el siguiente código en la versión 7
getEvolutionLife() {
      return this.db2.list('/esperanza_vida').valueChanges()
  } 
Para la versión 9, simplemente hay que aplicar el siguiente código:
obtener_base_de_datos_realtime(): Promise<Array<any>>{
    const dbRef = ref(getDatabase());
    return get(child(dbRef, 'esperanza_vida')).then((snapshot) => {
      let result = []
      snapshot.forEach((doc) => {
       result.push(doc.toJSON()) //en caso de objetos json.
      });
      return result
    }).catch((error) => {
      return []
    });
  }
Si utilizamos fire cloud, el código necesario sería el siguiente
getProyectos() {
    return this.db.collection('proyecto').snapshotChanges();
  }
Y en su versión 9 corresponde al siguiente código.
async obtener_colleccion() {
    const firebaseApp = initializeApp(environment.firebase);
    const db = getFirestore(firebaseApp);
    const q = query(collection(db, "proyecto"));
    let array = []
    const querySnapshot = await getDocs(q);
    querySnapshot.forEach((doc) => {
      array.push( doc.data())

    });
    return array
  }
Para terminar, os expongo un ejemplo de comunicación con firebase. En este ejemplo invocamos el método obtener_base_de_datos_realtime() de forma síncrona. Para realizar esta tarea, en el mismo código invocamos el método then y finally de la siguiente forma:
   this.graficaListData.obtener_base_de_datos_realtime().then(
          tratados => {
            for (let i = 0; i < tratados.length; i++) {
              this.tratados.push(<Tratados>tratados[i]);
            }
          }
          , error => this.errorMessage = <any>error);