División de código con importaciones dinámicas en Next.js

Cómo acelerar tu app de Next.js con división de código y estrategias de carga inteligente.

¿Qué aprenderás?

En esta entrada, se explican los diferentes tipos de división de código y cómo usar las importaciones dinámicas para acelerar tus apps de Next.js.

División de código basada en rutas y en componentes

De forma predeterminada, Next.js divide tu JavaScript en fragmentos separados para cada ruta. Cuando los usuarios cargan tu aplicación, Next.js solo envía el código necesario para la ruta inicial. Cuando los usuarios navegan por la app, recuperan los fragmentos asociados con las otras rutas. La división de código basada en rutas minimiza la cantidad de secuencias de comandos que se deben analizar y compilar a la vez, lo que acelera los tiempos de carga de la página.

Si bien la división de código basada en rutas es una buena opción predeterminada, puedes optimizar aún más el proceso de carga con la división de código a nivel de los componentes. Si tienes componentes grandes en tu app, te recomendamos dividirlos en fragmentos separados. De esa manera, cualquier componente grande que no sea fundamental o que solo se renderice en ciertas interacciones del usuario (como hacer clic en un botón) se puede cargar de forma diferida.

Next.js admite import() dinámico, que te permite importar módulos de JavaScript (incluidos los componentes de React) de forma dinámica y cargar cada importación como un fragmento separado. De esta manera, puedes dividir el código a nivel de los componentes y controlar la carga de recursos, de modo que los usuarios solo descarguen el código que necesitan para la parte del sitio que están viendo. En Next.js, estos componentes se renderizan en el servidor (SSR) de forma predeterminada.

Importaciones dinámicas en acción

Esta publicación incluye varias versiones de una app de ejemplo que consta de una página simple con un botón. Cuando haces clic en el botón, puedes ver un adorable cachorro. A medida que avances por cada versión de la app, verás en qué se diferencian las importaciones dinámicas de las importaciones estáticas y cómo trabajar con ellas.

En la primera versión de la app, el cachorro vive en components/Puppy.js. Para mostrar el cachorro en la página, la app importa el componente Puppy en index.js con una sentencia de importación estática:

import Puppy from "../components/Puppy";

Para ver cómo Next.js empaqueta la app, inspecciona el seguimiento de red en Herramientas para desarrolladores:

  1. Para obtener una vista previa del sitio, presiona Ver app. Luego, presiona Pantalla completa pantalla completa.

  2. Presiona "Control + Mayús + J" (o bien "Comando + Opción + J" en Mac) para abrir Herramientas para desarrolladores.

  3. Haga clic en la pestaña Red.

  4. Selecciona la casilla de verificación Inhabilitar caché.

  5. Vuelve a cargar la página.

Cuando cargas la página, todo el código necesario, incluido el componente Puppy.js, se agrupa en index.js:

La pestaña Network de Herramientas para desarrolladores muestra seis archivos JavaScript: index.js, app.js, webpack.js, main.js, 0.js y el archivo dll (biblioteca de vínculos dinámicos).

Cuando presionas el botón Haz clic, solo se agrega la solicitud del cachorro JPEG a la pestaña Red:

Pestaña Network de Herramientas para desarrolladores después del clic en el botón, en la que se muestran los mismos seis archivos JavaScript y una imagen.

La desventaja de este enfoque es que, incluso si los usuarios no hacen clic en el botón para ver al cachorro, deben cargar el componente Puppy porque está incluido en index.js. En este pequeño ejemplo, eso no es un gran problema, pero en las aplicaciones del mundo real, a menudo es una gran mejora cargar componentes grandes solo cuando es necesario.

Ahora, revisa una segunda versión de la app, en la que la importación estática se reemplaza por una importación dinámica. Next.js incluye next/dynamic, que permite usar importaciones dinámicas para cualquier componente en Next:

import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";

// ...

const Puppy = dynamic(import("../components/Puppy"));

Sigue los pasos del primer ejemplo para inspeccionar el seguimiento de red.

Cuando cargas la app por primera vez, solo se descarga index.js. Esta vez, es 0.5 KB más pequeño (disminuyó de 37.9 KB a 37.4 KB) porque no incluye el código del componente Puppy:

La red de Herramientas para desarrolladores muestra los mismos seis archivos JavaScript, excepto que index.js ahora es 0.5 KB más pequeño.

El componente Puppy ahora está en un fragmento separado, 1.js, que se carga solo cuando presionas el botón:

Pestaña Network de Herramientas para desarrolladores después de hacer clic en el botón, que muestra el archivo 1.js adicional y la imagen agregada a la parte inferior de la lista de archivos.

En las aplicaciones reales, los componentes suelen ser mucho más grandes, y su carga diferida puede reducir cientos de kilobytes de tu carga útil inicial de JavaScript.

Importaciones dinámicas con indicador de carga personalizado

Cuando realizas una carga diferida de recursos, se recomienda proporcionar un indicador de carga en caso de que haya algún retraso. En Next.js, puedes hacerlo proporcionando un argumento adicional a la función dynamic():

const Puppy = dynamic(() => import("../components/Puppy"), {
  loading: () => <p>Loading...</p>
});

Para ver el indicador de carga en acción, simula una conexión de red lenta en Herramientas para desarrolladores:

  1. Para obtener una vista previa del sitio, presiona Ver app. Luego, presiona Pantalla completa pantalla completa.

  2. Presiona "Control + Mayús + J" (o bien "Comando + Opción + J" en Mac) para abrir Herramientas para desarrolladores.

  3. Haga clic en la pestaña Red.

  4. Selecciona la casilla de verificación Inhabilitar caché.

  5. En la lista desplegable Regulación, selecciona 3G rápida.

  6. Presiona el botón Haz clic aquí.

Ahora, cuando haces clic en el botón, el componente tarda un poco en cargarse y la app muestra el mensaje "Cargando...".

Una pantalla oscura con el texto

Importaciones dinámicas sin SSR

Si necesitas renderizar un componente solo en el cliente (por ejemplo, un widget de chat), puedes hacerlo configurando la opción ssr como false:

const Puppy = dynamic(() => import("../components/Puppy"), {
  ssr: false,
});

Conclusión

Gracias a la compatibilidad con importaciones dinámicas, Next.js te proporciona una división de código a nivel de los componentes, lo que puede minimizar las cargas útiles de JavaScript y mejorar el tiempo de carga de la aplicación. De forma predeterminada, todos los componentes se renderizan en el servidor, y puedes inhabilitar esta opción siempre que sea necesario.