Divisão de código com importações dinâmicas no Next.js

Como acelerar seu app Next.js com divisão de código e estratégias de carregamento inteligentes.

O que você aprenderá?

Esta postagem explica diferentes tipos de código divisão e como usar importações dinâmicas para acelerar seus apps Next.js.

Divisão de código com base em rota e em componente

Por padrão, o Next.js divide seu JavaScript em partes separadas para cada rota. Quando os usuários carregam seu aplicativo, o Next.js envia apenas o código necessário para o rota inicial. Quando os usuários navegam pelo aplicativo, eles buscam os blocos associados às outras rotas. A divisão de código com base em rota minimiza a de script que precisa ser analisado e compilado de uma só vez, o que resulta carregamento da página mais rápido.

Embora a divisão de código com base em rota seja um bom padrão, é possível otimizar ainda mais de dados com divisão de código no nível do componente. Se você tem componentes em seu aplicativo, é uma ótima ideia dividi-los em partes separadas. Dessa forma, componentes grandes que não sejam críticos ou renderizados apenas em certos as interações do usuário (como clicar em um botão) podem ter carregamento lento.

O Next.js oferece suporte ao import() dinâmico, que permite importar módulos JavaScript (incluindo componentes do React) dinamicamente e carregue cada importação como um bloco separado. Isso dá a você a divisão de código em nível de componente e permite controlar o carregamento de recursos para que que os usuários façam o download apenas do código necessário para a parte do site que que eles estão visualizando. No Next.js, esses componentes são renderizados do lado do servidor (SSR) por padrão.

Importações dinâmicas em ação

Esta postagem inclui várias versões de um app de exemplo que consiste em um página com um botão. Ao clicar no botão, você vê um cachorrinho fofo. Conforme ao analisar cada versão do app, você verá como as importações dinâmicas diferente das métricas estáticas importações e como trabalhar com elas.

Na primeira versão do app, o cachorrinho mora em components/Puppy.js. Para mostrar o filhote na página, o app vai importar o componente Puppy para index.js com uma instrução de importação estática:

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

Para ver como o Next.js agrupa o app, inspecione o rastreamento de rede no DevTools:

  1. Para visualizar o site, pressione Ver app. Em seguida, pressione Tela cheia tela cheia

  2. Pressione "Control+Shift+J" (ou "Command+Option+J" no Mac) para abrir o DevTools.

  3. Clique na guia Rede.

  4. Marque a caixa de seleção Desativar cache.

  5. Recarregue a página.

Ao carregar a página, todo o código necessário, incluindo Puppy.js é empacotado em index.js:

Guia "Network" do DevTools mostrando seis arquivos JavaScript: index.js, app.js, webpack.js, main.js, 0.js e o arquivo dll (biblioteca de link dinâmico).

Quando você pressiona o botão Click me, apenas a solicitação para o arquivo JPEG de um filhote é adicionados à guia Rede:

Guia "Network" do DevTools após o clique do botão, mostrando os mesmos seis arquivos JavaScript e uma imagem.

A desvantagem dessa abordagem é que, mesmo que os usuários não cliquem no botão para ver o filhote, ele precisa carregar o componente Puppy porque ele está incluído no index.js. Neste pequeno exemplo, isso não é grande coisa, mas no mundo real aplicativos, carregar componentes grandes só quando necessários.

Agora confira uma segunda versão do aplicativo, na qual a importação estática é por uma importação dinâmica. O Next.js inclui next/dynamic, o que o torna é possível usar importações dinâmicas para qualquer componente no Next:

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

// ...

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

Siga as etapas do primeiro exemplo para inspecionar o rastreamento de rede.

No primeiro carregamento do app, apenas o index.js é transferido por download. Desta vez, 0,5 KB menor (diminuiu de 37,9 KB para 37,4 KB) porque não inclui o código do componente Puppy:

A rede do DevTools mostrando os mesmos seis arquivos JavaScript, mas o index.js agora é 0,5 KB menor.

O componente Puppy agora está em um bloco separado, 1.js, que é carregado apenas ao pressionar o botão:

Guia Network do DevTools após o clique do botão, mostrando o arquivo 1.js adicional e a imagem adicionada ao fim da lista de arquivos.

Em aplicações reais, os componentes são muitas vezes muito maiores, e o carregamento lento pode reduzir seu payload inicial de JavaScript em centenas de kilobytes.

Importações dinâmicas com indicador de carregamento personalizado

Ao carregar recursos lentamente, é recomendável fornecer um indicador de carregamento caso haja atrasos. No Next.js, você pode fazer isso fornecendo uma argumento extra para a função dynamic():

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

Para ver o indicador de carregamento em ação, simule uma conexão de rede lenta no DevTools:

  1. Para visualizar o site, pressione Ver app. Em seguida, pressione Tela cheia tela cheia

  2. Pressione "Control+Shift+J" (ou "Command+Option+J" no Mac) para abrir o DevTools.

  3. Clique na guia Rede.

  4. Marque a caixa de seleção Desativar cache.

  5. Na lista suspensa Limitação, selecione 3G rápido.

  6. Pressione o botão Clique aqui.

Agora, quando você clica no botão, demora um pouco para carregar o componente e o app. exibe a mensagem "Carregando..." enquanto isso.

Uma tela escura com texto

Importações dinâmicas sem SSR

Se você precisar renderizar um componente somente no lado do cliente (por exemplo, um widget), defina a opção ssr como false:

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

Conclusão

Com suporte a importações dinâmicas, o Next.js fornece códigos no nível do componente o que pode minimizar seus payloads de JavaScript e melhorar o desempenho tempo de carregamento atual. Todos os componentes são renderizados no servidor por padrão, e é possível desative essa opção sempre que necessário.