Saltar al contenido principal

[Lv1] Optimización a nivel de rutas: Lazy Loading en 3 niveles

Mediante Lazy Loading de rutas en 3 niveles, se redujo la carga inicial de 12.5MB a 850KB, acortando el tiempo de primera pantalla en un 70%.


Contexto del problema (Situation)​

Características del proyecto:

  • 27+ plantillas multi-tenant diferentes (arquitectura multi-tenant)
  • Cada plantilla tiene 20-30 páginas (inicio, lobby, promociones, agentes, noticias, etc.)
  • Si se carga todo el código de una vez: Se necesita descargar 10MB+ de archivos JS en la primera entrada
  • Tiempo de espera del usuario superior a 10 segundos (especialmente en redes móviles)

Objetivo de optimización (Task)​

  1. Reducir el volumen de JavaScript en la carga inicial (objetivo: < 1MB)
  2. Acortar el tiempo de primera pantalla (objetivo: < 3 segundos)
  3. Carga bajo demanda (el usuario solo descarga lo que necesita)
  4. Mantener la experiencia de desarrollo (sin afectar la eficiencia de desarrollo)

Solución (Action)​

Adoptamos una estrategia de Lazy Loading de rutas en 3 niveles, optimizando desde "plantilla" → "página" → "permisos".

Nivel 1: Carga dinámica de plantillas​

// src/router/routes.ts
export default async function (siteKey?: string) {
// Cargar dinámicamente las rutas de la plantilla correspondiente según variables de entorno
const module = await import(`../../template/${siteKey}/router/routes.ts`);
return { routes: module.routes };
}

Explicacion:

  • El proyecto tiene 27 plantillas, pero el usuario solo usa 1
  • Se determina la marca actual mediante environment.json
  • Solo se carga la configuración de rutas de esa marca, las otras 26 plantillas no se cargan en absoluto

Efecto:

  • Reducción de aproximadamente el 85% del código de configuración de rutas en la carga inicial

Nivel 2: Lazy Loading de páginas​

// Forma tradicional (X - no recomendada)
import HomePage from './pages/HomePage.vue';
component: HomePage; // Todas las páginas se empaquetan en main.js

// Nuestra forma (✓ - recomendada)
component: () => import('app/template/okbet_green/pages/HomePage/Home.vue');
  • Cada ruta usa función flecha + import()
  • Solo cuando el usuario visita realmente esa página se descarga el chunk JS correspondiente
  • Vite empaqueta automáticamente cada página como un archivo independiente

Nivel 3: Estrategia de carga bajo demanda​

// src/router/index.ts
router.beforeEach((to, from, next) => {
const { needAuth } = to.meta;
if (needAuth && !store.isLogin) {
// Usuarios no autenticados no cargan páginas que requieren login como "Centro de agentes"
return next({ name: 'HomePage' });
}
next();
});

Resultados de la optimización (Result)​

Antes de la optimización:

Carga inicial: main.js (12.5 MB)
Tiempo primera pantalla: 8-12 segundos
Incluye las 27 plantillas + todas las páginas

Después de la optimización:

Carga inicial: main.js (850 KB) ↓ 93%
Tiempo primera pantalla: 1.5-2.5 segundos ↑ 70%
Solo código esencial + página de inicio actual

Datos concretos:

  • Reducción de JavaScript: 12.5 MB → 850 KB (reducción del 93%)
  • Reducción del tiempo de primera pantalla: 10 segundos → 2 segundos (mejora del 70%)
  • Carga de páginas posteriores: Promedio 300-500 KB por página
  • Puntuacion de experiencia de usuario: De 45 a 92 puntos (Lighthouse)

Valor comercial:

  • Tasa de rebote reducida en 35%
  • Tiempo de permanencia aumentado en 50%
  • Tasa de conversión mejorada en 25%

Puntos clave para entrevistas​

Preguntas de extensión frecuentes:

  1. P: Por que no usar React.lazy() o componentes asincronos de Vue? R: De hecho usamos componentes asincronos de Vue (() => import()), pero la clave es la arquitectura de 3 niveles:

    • Nivel 1 (plantilla): Determinado en tiempo de compilación (configuración Vite)
    • Nivel 2 (página): Lazy Loading en tiempo de ejecución
    • Nivel 3 (permisos): Control por Navigation Guard

    Un Lazy Loading simple solo llega al nivel 2; nosotros agregamos la separación a nivel de plantilla.

  2. P: Cómo se decide qué código poner en main.js? R: Usando la configuración manualChunks de Vite:

    build: {
    rollupOptions: {
    output: {
    manualChunks: {
    'vendor': ['vue', 'pinia', 'vue-router'],
    'ui': ['element-plus'],
    }
    }
    }
    }

    Principio: Solo lo que "todas las páginas usan" va en el vendor chunk.

  3. P: El Lazy Loading no afecta la experiencia del usuario (tiempo de espera)? R: Dos estrategias para contrarrestarlo:

    • Prefetch: Precargar páginas que probablemente se visitaran durante el tiempo inactivo
    • Estado de carga: Usar Skeleton Screen en lugar de pantalla blanca

    Pruebas reales: Tiempo promedio de carga de páginas posteriores < 500ms, imperceptible para el usuario.

  4. P: Cómo se mide el efecto de la optimización? R: Usando múltiples herramientas:

    • Lighthouse: Performance Score (45 → 92)
    • Webpack Bundle Analyzer: Analisis visual del tamaño de chunks
    • Chrome DevTools: Network waterfall, Coverage
    • Real User Monitoring (RUM): Datos de usuarios reales
  5. P: Cuáles son los Trade-offs? R:

    • Posibles problemas de dependencias circulares durante el desarrollo (requiere ajustar estructura de modulos)
    • Breve tiempo de carga en el primer cambio de ruta (resuelto con prefetch)
    • Pero en general los beneficios superan las desventajas, especialmente la mejora notable en la experiencia de usuarios móviles