Análisis de tamaño de paquete
Visualizador de paquetes Metro y división de rutas lazy - encuentra qué se envía en el paquete Hermes principal y difiere pantallas pesadas hasta después del inicio en frío.
Busca en todas las páginas de la documentación
Visualizador de paquetes Metro y división de rutas lazy - encuentra qué se envía en el paquete Hermes principal y difiere pantallas pesadas hasta después del inicio en frío.
Tarjeta de referencia rápida - lista para copiar y pegar.
1. Genera paquete de producción y mapa de fuentes
npx react-native bundle \
--platform android \
--dev false \
--entry-file node_modules/expo-router/entry.js \
--bundle-output ./.perf/index.android.bundle \
--sourcemap-output ./.perf/index.android.bundle.map \
--assets-dest ./.perf/assets2. Abre treemap
npx react-native-bundle-visualizer \
--bundle ./.perf/index.android.bundle \
--sourcemap ./.perf/index.android.bundle.map3. Triage de principales infractores
| Bloque treemap | Corrección común |
|---|---|
moment / date-fns/locale/* | dayjs o importaciones estrechas |
lodash completo | lodash/es/map o helpers nativos |
@expo/vector-icons conjunto completo | Importación por familia o fuente de subconjunto |
charting / maps / editor SDKs | Ruta lazy + import() dinámico |
react duplicado / copias anidadas | Alinea metro.config.js en monorepo |
4. Carga lazy de pantallas pesadas de Expo Router
// app/(tabs)/analytics.tsx - pantalla de pestaña lazy
import { Suspense, lazy } from "react";
import { ActivityIndicator, View } from "react-native";
const AnalyticsScreen = lazy(() => import("../../src/screens/AnalyticsScreen"));
export default function AnalyticsRoute() {
return (
<Suspense
fallback={
<View style={{ flex: 1, justifyContent: "center" }}>
<ActivityIndicator />
</View>
}
>
<AnalyticsScreen />
</Suspense>
);
}5. Puerta de tamaño de CI (esquema)
#!/usr/bin/env bash
# scripts/check-bundle-size.sh
BUNDLE="./.perf/index.android.bundle"
MAX_BYTES=4500000 # ~4.3 MB raw - ajusta a tu presupuesto gzip
SIZE=$(wc -c < "$BUNDLE")
if [ "$SIZE" -gt "$MAX_BYTES" ]; then
echo "Bundle $SIZE exceeds $MAX_BYTES"
exit 1
fiCuándo usar esto:
import desde un SDK pesado en la ruta del layout raíz.Audita una importación raíz hinchada, divide una pantalla pesada y verifica que el bloque treemap se mueva fuera de la ruta principal.
// app/_layout.tsx - mantén raíz ligera
import { Stack } from "expo-router";
import { QueryClientProvider } from "@tanstack/react-query";
import { queryClient } from "../src/lib/queryClient";
export default function RootLayout() {
return (
<QueryClientProvider client={queryClient}>
<Stack screenOptions={{ headerShown: false }} />
</QueryClientProvider>
);
}// app/settings/reports.tsx - pantalla pesada NO en ruta de TTI
import { Suspense, lazy } from "react";
import { ActivityIndicator, StyleSheet, View } from "react-native";
const ReportsScreen = lazy(() =>
import("../../src/screens/ReportsScreen").then((m) => ({ default: m.ReportsScreen })),
);
export default function ReportsRoute() {
return (
<Suspense fallback={<View style={styles.fallback}><ActivityIndicator /></View>}>
<ReportsScreen />
</Suspense>
);
}
const styles = StyleSheet.create({
fallback: { flex: 1, alignItems: "center", justifyContent: "center" },
});// src/screens/ReportsScreen.tsx - deps pesadas aisladas a fragmento lazy
import { StyleSheet, Text, View } from "react-native";
// Ejemplo de dep pesada - carga solo cuando el usuario abre Reports
import { VictoryPie } from "victory-native";
export function ReportsScreen() {
const data = [
{ x: "A", y: 35 },
{ x: "B", y: 25 },
{ x: "C", y: 40 },
];
return (
<View style={styles.screen}>
<Text style={styles.title}>Reports</Text>
<VictoryPie data={data} width={280} height={280} />
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, alignItems: "center" },
title: { fontSize: 20, fontWeight: "700", marginBottom: 16 },
});Lista de verificación de verificación
victory-native (o tu SDK) área en paquete principal.lazy() - vuelve a empaquetar; bloque pesado se encoge en treemap principal.import el módulo pesado transitivamente a través de barrels.// MALO - extrae biblioteca completa
import _ from "lodash";
import * as Icons from "@expo/vector-icons";
// MEJOR
import debounce from "lodash/debounce";
import Ionicons from "@expo/vector-icons/Ionicons";// packages/ui/src/index.ts - re-exporta todo
export * from "./charts"; // ← arrastra charts en cualquier importación `from "@acme/ui"`
// MEJOR - subpaths explícitos en package.json exports
// import { Button } from "@acme/ui/button";Paquetes compartidos en monorepos duplican dependencias fácilmente - alinea watchFolders y resolver en metro.config.js. Ver Shared Packages & Metro Resolution.
| Patrón | Impacto de TTI |
|---|---|
Módulo de pantalla lazy() | Fragmento carga en primera navegación - bien para settings/reports |
Pestaña lazy: true (predeterminado) | Difiere primer montaje hasta enfoque de pestaña - bien para inicio en frío |
Link prefetch | Carga fragmento temprano - usa solo para pantalla siguiente probable |
Importaciones _layout raíz | Siempre en ruta crítica - mantén mínimo |
// Precarga ruta de detalle pesada cuando usuario pasa sobre tarjeta - compensación opcional
import { Link } from "expo-router";
<Link href="/reports" prefetch>
Open reports
</Link>Assets (imágenes, fuentes) son separados pero afectan descarga - media incluida expo-asset aparece en salida de exportación.
dev: true incluye dev tooling; siempre usa --dev false.lazy().Ambos mapean bytes a módulos. react-native-bundle-visualizer está sintonizado para salida Metro RN - comienza allí. Usa source-map-explorer si ya tienes tooling web en CI.
CI a menudo cierra raw o gzip - elige uno y documéntalo. Gzip ≈ 30–40% de raw para paquetes RN típicos; alinea con tu definición de presupuesto de 4 MB.
Metro soporta import() para fragmentos async. Módulos nativos aún autolink en tiempo de compilación - eliminar importación JS no elimina código nativo a menos que desinstales el paquete.
No - solo importaciones alcanzables desde entrada de producción cuentan. Aún afectan instalación y tiempo de CI - ver Dead Code & Dependency Analysis.
Archivos .ttf empaquetados aterrizan en assets. Subset icon fonts y carga display fonts después de splash cuando sea posible - Startup Time Optimization.
TTI incluye startup nativo, carga bytecode y trabajo de primer pantalla - no solo bytes JS. Perfil holísticamente con Performance Basics.
lazyVersiones de Stack: Esta página fue escrita para React 19.2.3, React Native 0.86.0 y Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 16 jul 2026