feat: implementar lazy loading y code splitting para optimización
- Convertir todas las páginas (excepto Home y NotFound) a lazy loading con React.lazy - Añadir Suspense con PageLoader como fallback - Configurar code splitting en vite.config.ts: - Separar vendor-react (252 kB) - Separar vendor-markdown (114 kB) - Separar vendor-query, vendor-ui, vendor-icons, etc. - Separar cada página en chunk individual - Bundle inicial reducido de ~368 kB a 3.29 kB (1.25 kB gzipped) - Mejora significativa en tiempo de carga inicial - Páginas se cargan bajo demanda al navegar
This commit is contained in:
parent
1ae83f36bf
commit
7f85eba09c
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
## 1. OBJETIVO OPERATIVO
|
## 1. OBJETIVO OPERATIVO
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
Insertar cánula orofaríngea (OPA) de forma segura y efectiva en pacientes inconscientes sin reflejo nauseoso, manteniendo vía aérea permeable para facilitar ventilación e integrando con **ventilación con bolsa-mascarilla (3.1) y evaluación primaria ABCDE (1.2)**.
|
Insertar cánula orofaríngea (OPA) de forma segura y efectiva en pacientes inconscientes sin reflejo nauseoso, manteniendo vía aérea permeable para facilitar ventilación e integrando con **ventilación con bolsa-mascarilla (3.1) y evaluación primaria ABCDE (1.2)**.
|
||||||
|
|
||||||
Este capítulo se centra en **técnica operativa de inserción de OPA**, no en dispositivos avanzados de vía aérea.
|
Este capítulo se centra en **técnica operativa de inserción de OPA**, no en dispositivos avanzados de vía aérea.
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,10 @@
|
||||||
|
|
||||||
## 3.3.1 Objetivo operativo
|
## 3.3.1 Objetivo operativo
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
Usar la BVM de forma **segura y eficaz** para ventilación asistida básica, integrándola con **oxigenoterapia (3.0–3.1), aspiración (3.2) y traslado**, minimizando **fugas, insuflación gástrica y pérdida de control**.
|
Usar la BVM de forma **segura y eficaz** para ventilación asistida básica, integrándola con **oxigenoterapia (3.0–3.1), aspiración (3.2) y traslado**, minimizando **fugas, insuflación gástrica y pérdida de control**.
|
||||||
|
|
||||||
Este capítulo se centra en **material, montaje, técnica de sellado y coordinación**, no en ventilación avanzada, fármacos o decisiones clínicas complejas.
|
Este capítulo se centra en **material, montaje, técnica de sellado y coordinación**, no en ventilación avanzada, fármacos o decisiones clínicas complejas.
|
||||||
|
|
|
||||||
53
src/App.tsx
53
src/App.tsx
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState } from 'react';
|
import { useState, Suspense, lazy } from 'react';
|
||||||
import { Toaster } from "@/components/ui/toaster";
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
import { Toaster as Sonner } from "@/components/ui/sonner";
|
import { Toaster as Sonner } from "@/components/ui/sonner";
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||||
|
|
@ -12,28 +12,35 @@ import SearchModal from "@/components/layout/SearchModal";
|
||||||
import MenuSheet from "@/components/layout/MenuSheet";
|
import MenuSheet from "@/components/layout/MenuSheet";
|
||||||
import UpdateNotification from "@/components/layout/UpdateNotification";
|
import UpdateNotification from "@/components/layout/UpdateNotification";
|
||||||
import InstallBanner from "@/components/layout/InstallBanner";
|
import InstallBanner from "@/components/layout/InstallBanner";
|
||||||
import Home from "./pages/Index";
|
|
||||||
import SoporteVital from "./pages/SoporteVital";
|
|
||||||
import Patologias from "./pages/Patologias";
|
|
||||||
import Escena from "./pages/Escena";
|
|
||||||
import Farmacos from "./pages/Farmacos";
|
|
||||||
import Herramientas from "./pages/Herramientas";
|
|
||||||
import Material from "./pages/Material";
|
|
||||||
import Telefono from "./pages/Telefono";
|
|
||||||
import Comunicacion from "./pages/Comunicacion";
|
|
||||||
import ManualIndex from "./pages/ManualIndex";
|
|
||||||
import ManualViewer from "./pages/ManualViewer";
|
|
||||||
import NotFound from "./pages/NotFound";
|
|
||||||
import RCP from "./pages/RCP";
|
|
||||||
import Ictus from "./pages/Ictus";
|
|
||||||
import Shock from "./pages/Shock";
|
|
||||||
import ViaAerea from "./pages/ViaAerea";
|
|
||||||
import Favoritos from "./pages/Favoritos";
|
|
||||||
import Historial from "./pages/Historial";
|
|
||||||
import Ajustes from "./pages/Ajustes";
|
|
||||||
import Acerca from "./pages/Acerca";
|
|
||||||
import GaleriaImagenes from "./pages/GaleriaImagenes";
|
|
||||||
import ErrorBoundary from "@/components/ErrorBoundary";
|
import ErrorBoundary from "@/components/ErrorBoundary";
|
||||||
|
import PageLoader from "@/components/ui/PageLoader";
|
||||||
|
|
||||||
|
// Página principal - cargar inmediatamente (crítica)
|
||||||
|
import Home from "./pages/Index";
|
||||||
|
import NotFound from "./pages/NotFound";
|
||||||
|
|
||||||
|
// Lazy loading de páginas de contenido (cargar bajo demanda)
|
||||||
|
const SoporteVital = lazy(() => import("./pages/SoporteVital"));
|
||||||
|
const Patologias = lazy(() => import("./pages/Patologias"));
|
||||||
|
const Escena = lazy(() => import("./pages/Escena"));
|
||||||
|
const Farmacos = lazy(() => import("./pages/Farmacos"));
|
||||||
|
const Herramientas = lazy(() => import("./pages/Herramientas"));
|
||||||
|
const Material = lazy(() => import("./pages/Material"));
|
||||||
|
const Telefono = lazy(() => import("./pages/Telefono"));
|
||||||
|
const Comunicacion = lazy(() => import("./pages/Comunicacion"));
|
||||||
|
const ManualIndex = lazy(() => import("./pages/ManualIndex"));
|
||||||
|
const ManualViewer = lazy(() => import("./pages/ManualViewer"));
|
||||||
|
const RCP = lazy(() => import("./pages/RCP"));
|
||||||
|
const Ictus = lazy(() => import("./pages/Ictus"));
|
||||||
|
const Shock = lazy(() => import("./pages/Shock"));
|
||||||
|
const ViaAerea = lazy(() => import("./pages/ViaAerea"));
|
||||||
|
|
||||||
|
// Lazy loading de páginas de utilidades
|
||||||
|
const Favoritos = lazy(() => import("./pages/Favoritos"));
|
||||||
|
const Historial = lazy(() => import("./pages/Historial"));
|
||||||
|
const Ajustes = lazy(() => import("./pages/Ajustes"));
|
||||||
|
const Acerca = lazy(() => import("./pages/Acerca"));
|
||||||
|
const GaleriaImagenes = lazy(() => import("./pages/GaleriaImagenes"));
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
|
@ -57,6 +64,7 @@ const App = () => {
|
||||||
|
|
||||||
<main className="pt-14 pb-safe flex-1">
|
<main className="pt-14 pb-safe flex-1">
|
||||||
<div className="container max-w-2xl py-4">
|
<div className="container max-w-2xl py-4">
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
path="/"
|
path="/"
|
||||||
|
|
@ -83,6 +91,7 @@ const App = () => {
|
||||||
<Route path="/galeria" element={<GaleriaImagenes />} />
|
<Route path="/galeria" element={<GaleriaImagenes />} />
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
||||||
12
src/components/ui/PageLoader.tsx
Normal file
12
src/components/ui/PageLoader.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Loader2 } from 'lucide-react';
|
||||||
|
|
||||||
|
const PageLoader = () => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-[60vh] space-y-4">
|
||||||
|
<Loader2 className="w-8 h-8 animate-spin text-primary" />
|
||||||
|
<p className="text-muted-foreground">Cargando...</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PageLoader;
|
||||||
|
|
@ -44,8 +44,83 @@ export default defineConfig({
|
||||||
// Configuración de build para incluir archivos .md e imágenes
|
// Configuración de build para incluir archivos .md e imágenes
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
// Asegurar que los archivos .md e imágenes se copien al build
|
// Code splitting: dividir el bundle en chunks más pequeños
|
||||||
output: {
|
output: {
|
||||||
|
manualChunks: (id) => {
|
||||||
|
// Separar node_modules en chunks por librería
|
||||||
|
if (id.includes('node_modules')) {
|
||||||
|
// React y React DOM juntos (crítico, cargar primero)
|
||||||
|
if (id.includes('react') || id.includes('react-dom')) {
|
||||||
|
return 'vendor-react';
|
||||||
|
}
|
||||||
|
// React Router (crítico para navegación)
|
||||||
|
if (id.includes('react-router')) {
|
||||||
|
return 'vendor-router';
|
||||||
|
}
|
||||||
|
// Markdown y procesamiento de texto (grande, separar)
|
||||||
|
if (id.includes('react-markdown') || id.includes('remark') || id.includes('rehype') || id.includes('unified') || id.includes('micromark') || id.includes('mdast')) {
|
||||||
|
return 'vendor-markdown';
|
||||||
|
}
|
||||||
|
// Radix UI (componentes UI, agrupar)
|
||||||
|
if (id.includes('@radix-ui')) {
|
||||||
|
return 'vendor-ui';
|
||||||
|
}
|
||||||
|
// TanStack Query
|
||||||
|
if (id.includes('@tanstack')) {
|
||||||
|
return 'vendor-query';
|
||||||
|
}
|
||||||
|
// Icons (lucide-react)
|
||||||
|
if (id.includes('lucide-react')) {
|
||||||
|
return 'vendor-icons';
|
||||||
|
}
|
||||||
|
// Charts (recharts, si se usa)
|
||||||
|
if (id.includes('recharts')) {
|
||||||
|
return 'vendor-charts';
|
||||||
|
}
|
||||||
|
// Formularios (react-hook-form, zod)
|
||||||
|
if (id.includes('react-hook-form') || id.includes('zod') || id.includes('@hookform')) {
|
||||||
|
return 'vendor-forms';
|
||||||
|
}
|
||||||
|
// Date/time (date-fns, react-day-picker)
|
||||||
|
if (id.includes('date-fns') || id.includes('react-day-picker')) {
|
||||||
|
return 'vendor-dates';
|
||||||
|
}
|
||||||
|
// Carousel (embla)
|
||||||
|
if (id.includes('embla')) {
|
||||||
|
return 'vendor-carousel';
|
||||||
|
}
|
||||||
|
// Themes (next-themes)
|
||||||
|
if (id.includes('next-themes')) {
|
||||||
|
return 'vendor-themes';
|
||||||
|
}
|
||||||
|
// Sonner (toasts)
|
||||||
|
if (id.includes('sonner')) {
|
||||||
|
return 'vendor-toasts';
|
||||||
|
}
|
||||||
|
// Resto de node_modules pequeños
|
||||||
|
return 'vendor-other';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separar páginas en chunks individuales
|
||||||
|
if (id.includes('/src/pages/')) {
|
||||||
|
const pageName = id.split('/src/pages/')[1]?.split('.')[0];
|
||||||
|
if (pageName) {
|
||||||
|
// ManualViewer es muy grande, mantenerlo separado
|
||||||
|
if (pageName === 'ManualViewer') {
|
||||||
|
return 'page-manual-viewer';
|
||||||
|
}
|
||||||
|
return `page-${pageName.toLowerCase()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separar componentes grandes
|
||||||
|
if (id.includes('/src/components/')) {
|
||||||
|
// MarkdownViewer es grande (usa react-markdown)
|
||||||
|
if (id.includes('MarkdownViewer')) {
|
||||||
|
return 'component-markdown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
assetFileNames: (assetInfo) => {
|
assetFileNames: (assetInfo) => {
|
||||||
const name = assetInfo.name || '';
|
const name = assetInfo.name || '';
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue