Volver a la página principal
lunes 18 noviembre 2024
16

Cómo gestionar el almacenamiento del navegador en Angular SSR

El Renderizado del Lado del Servidor (SSR) en Angular plantea desafíos adicionales en comparación con el renderizado tradicional del lado del cliente, especialmente al manejar el almacenamiento del navegador. En esta guía exhaustiva, exploraremos cómo abordar estos desafíos en aplicaciones Angular SSR. Veremos los fundamentos de SSR, los problemas específicos relacionados con el almacenamiento del navegador y las estrategias para resolverlos. También incluiremos ejemplos de código y mejores prácticas para garantizar una experiencia de usuario fluida y eficiente.

Introducción a Angular SSR

El SSR en Angular permite renderizar aplicaciones Angular en el servidor, ofreciendo beneficios como un mejor rendimiento, optimización para motores de búsqueda (SEO) y tiempos de carga inicial más rápidos. Durante el SSR, la aplicación se renderiza en el servidor y se envía el HTML resultante al cliente, donde se arranca la aplicación Angular.

Conceptos clave de Angular SSR:

  • Renderizado del Lado del Servidor (SSR): El proceso de renderizar componentes de Angular en el servidor en lugar de en el navegador.
  • Renderizado Universal: Término utilizado como sinónimo de SSR en el contexto de Angular.
  • PlatformServer: Módulo de Angular que facilita el renderizado del lado del servidor.
  • TransferState: Servicio proporcionado por Angular para transferir estado del servidor al cliente durante el SSR.

Desafíos con el almacenamiento del navegador en SSR

El almacenamiento del navegador, que incluye mecanismos como localStorage y sessionStorage, presenta desafíos únicos en el contexto de SSR. Estos problemas surgen debido a la desconexión entre el servidor que renderiza la página inicial y el cliente que toma el control posteriormente.

Desafíos principales:

1. Falta del objeto Window: Durante el SSR, el objeto window no está disponible, lo que impide el acceso directo al almacenamiento del navegador.

2. Sincronización de datos: Es crucial sincronizar el contenido renderizado por el servidor con el almacenamiento del cliente.

3. Preocupaciones de seguridad: Manejar datos sensibles en el almacenamiento introduce riesgos de seguridad, especialmente durante el SSR.

Estrategias para manejar el almacenamiento del navegador en SSR

Para abordar estos desafíos, es necesario combinar lógica del lado del servidor y del cliente. A continuación, se presentan estrategias con ejemplos de código.

1. Usa TransferState para transferir datos iniciales

El servicio TransferState de Angular permite transferir estado desde el servidor al cliente, facilitando la inicialización del almacenamiento en el cliente.

Servidor:

// SSR con Angular Universal
import { TransferState } from '@angular/platform-browser';

app.get('*', (req, res) => {
  const state = { /* Datos iniciales */ };
  const transferState = new TransferState();
  transferState.setState(state);

  // Renderiza la aplicación Angular con SSR
  renderAngularApp(req, res, transferState);
});

Cliente:

import { TransferState } from '@angular/platform-browser';

export class AppComponent implements OnInit {
  constructor(private transferState: TransferState) {}

  ngOnInit() {
    // Recupera el estado inicial transferido desde el servidor
    const initialState = this.transferState.get<any>(STATE_KEY, null);

    // Usa el estado inicial para configurar el almacenamiento del cliente
    // ...
  }
}

2. Ejecución condicional en el cliente

Utiliza isPlatformBrowser e isPlatformServer para ejecutar código dependiendo de la plataforma.

import { PLATFORM_ID, Inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

constructor(@Inject(PLATFORM_ID) private platformId: Object) {}

ngOnInit() {
  if (isPlatformBrowser(this.platformId)) {
    // Código que se ejecuta solo en el cliente
    // ...
  }
}

3. Servicio de almacenamiento personalizado

Crea un servicio que abstraiga el acceso al almacenamiento del navegador, manejando la lógica específica de la plataforma.

import { Injectable, PLATFORM_ID, Inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

@Injectable({
  providedIn: 'root',
})
export class BrowserStorageService {
  private storage: Storage;

  constructor(@Inject(PLATFORM_ID) private platformId: Object) {
    this.storage = isPlatformBrowser(this.platformId) ? localStorage : null;
  }

  getItem(key: string): string | null {
    return this.storage ? this.storage.getItem(key) : null;
  }

  setItem(key: string, value: string): void {
    if (this.storage) {
      this.storage.setItem(key, value);
    }
  }
}

4. Carga diferida y inicialización asíncrona

Retrasa el acceso al almacenamiento hasta que la aplicación Angular esté completamente cargada en el cliente. Utiliza APP_INITIALIZER para inicializar el almacenamiento de forma asíncrona.

import { APP_INITIALIZER } from '@angular/core';

export function initializeStorage(storageService: BrowserStorageService): () => void {
  return () => storageService.initialize();
}

@NgModule({
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: initializeStorage,
      deps: [BrowserStorageService],
      multi: true,
    },
  ],
})
export class AppInitializerModule {}

Servicio:

initialize(): Promise<void> {
  return new Promise<void>((resolve) => {
    // Inicialización asíncrona del almacenamiento
    resolve();
  });
}

5. Manejo seguro de datos

Protege los datos sensibles mediante técnicas de cifrado o tokenización antes de almacenarlos en el cliente.

import * as CryptoJS from 'crypto-js';

const secretKey = 'clave-secreta';

const encryptedData = CryptoJS.AES.encrypt('datos-sensibles', secretKey).toString();

const decryptedData = CryptoJS.AES.decrypt(encryptedData, secretKey).toString(CryptoJS.enc.Utf8);

Mejores prácticas

1. Encapsula la lógica de almacenamiento: Utiliza un servicio dedicado para modularidad y mantenibilidad.

2. Comprueba la plataforma: Usa isPlatformBrowser y isPlatformServer para evitar errores al ejecutar código inapropiado.

3. Protege datos sensibles: Implementa cifrado para mejorar la seguridad.

4. Carga diferida: Optimiza el rendimiento retrasando el acceso al almacenamiento.

5. Considera las limitaciones: Ten en cuenta las restricciones de tamaño del almacenamiento y usa estrategias como compresión.

Conclusión

Manejar el almacenamiento del navegador en Angular SSR requiere una combinación de estrategias del lado del servidor y del cliente. Al comprender los desafíos, adoptar las estrategias adecuadas y seguir las mejores prácticas, puedes garantizar aplicaciones robustas, seguras y de alto rendimiento. Los ejemplos de código proporcionados te ayudarán a implementar estas estrategias de manera eficaz. La gestión cuidadosa del almacenamiento en Angular SSR es crucial para construir aplicaciones modernas y optimizadas.

Compartir:
Creado por:
Author photo

Jorge García

Fullstack developer