[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)
- Reducir el volumen de JavaScript en la carga inicial (objetivo: < 1MB)
- Acortar el tiempo de primera pantalla (objetivo: < 3 segundos)
- Carga bajo demanda (el usuario solo descarga lo que necesita)
- 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:
-
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.
-
P: Cómo se decide qué código poner en main.js? R: Usando la configuración
manualChunksde 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.
-
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.
-
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
-
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