JavaScript de divisão de código

O carregamento de recursos JavaScript grandes causa um impacto significativo na velocidade da página. Divisão seu JavaScript em partes menores e baixando apenas o que é necessário para o funcionamento de uma página durante a inicialização pode melhorar muito o carregamento dela capacidade de resposta, que por sua vez pode melhorar a Interação com o Next da sua página Paint (INP).

Enquanto uma página faz o download, analisa e compila grandes arquivos JavaScript, ela pode se tornar não responde por períodos de tempo. Os elementos da página são visíveis, como são uma parte do HTML inicial de uma página e estilizado por CSS. No entanto, como o JavaScript necessários para alimentar esses elementos interativos, bem como outros scripts carregados por da página, podem estar analisando e executando o JavaScript para que funcionem. A é que o usuário pode sentir que a interação foi significativamente atrasados ou até mesmo corrompidos.

Isso geralmente acontece porque a linha de execução principal está bloqueada enquanto o JavaScript é analisado e compilados na linha de execução principal. Se esse processo demorar muito, o processo interativo os elementos da página podem não responder rápido o suficiente à entrada do usuário. Uma solução para isso é carregar apenas o JavaScript necessário para que a página funcione, enquanto adiar outro JavaScript para carregar mais tarde por meio de uma técnica conhecida como código divisão. O foco deste módulo é a última dessas duas técnicas.

Reduza a análise e a execução do JavaScript durante a inicialização por meio da divisão de código.

O Lighthouse gera um aviso quando a execução do JavaScript demora mais de 2". segundos e falha quando leva mais de 3, 5 segundos. Excesso de JavaScript a análise e a execução são um problema potencial em qualquer ponto da página porque ele pode aumentar o atraso de entrada (link em inglês) de uma interação. se o momento em que o usuário interage com a página coincidir com o momento as principais tarefas da linha de execução responsáveis pelo processamento e execução do JavaScript são em execução.

Além disso, a execução e a análise excessivas de JavaScript são particularmente problemático durante o carregamento inicial da página, pois esse é o ponto na página ciclo de vida que os usuários são muito propensos a interagir com a página. Na verdade, Tempo total de bloqueio (TBT, na sigla em inglês), uma métrica de capacidade de resposta à carga, está altamente correlacionado com INP, sugerindo que os usuários têm uma alta tendência a tentar interações durante o carregamento inicial da página.

A auditoria do Lighthouse que relata o tempo gasto na execução de cada arquivo JavaScript que sua página solicita é útil, pois pode ajudar você a identificar exatamente quais scripts podem ser candidatos à divisão de código. Você pode ir mais longe usando a ferramenta de cobertura no Chrome DevTools para identificar exatamente quais partes o JavaScript de uma página não é utilizado durante o carregamento dela.

A divisão de código é uma técnica útil que pode reduzir o JavaScript inicial de uma página payloads. Ele permite dividir um pacote JavaScript em duas partes:

  • O JavaScript é necessário no carregamento da página e, portanto, não pode ser carregado em nenhum outro tempo de resposta.
  • JavaScript restante que pode ser carregado posteriormente, com mais frequência no ponto em que o usuário interage com um determinado elemento interativo no da página.

A divisão de código pode ser feita usando a sintaxe dinâmica import() (link em inglês). Isso Sintaxe, diferentemente dos elementos <script>, que solicitam um determinado recurso JavaScript. durante a inicialização, faz uma solicitação por um recurso JavaScript em um momento posterior do o ciclo de vida da página.

document.querySelectorAll('#myForm input').addEventListener('blur', async () => {
  // Get the form validation named export from the module through destructuring:
  const { validateForm } = await import('/validate-form.mjs');

  // Validate the form:
  validateForm();
}, { once: true });

No snippet JavaScript anterior, o módulo validate-form.mjs é transferidos por download, analisados e executados somente quando um usuário desfoca qualquer elemento <input>. Nessa situação, o recurso JavaScript responsável a lógica de validação do formulário só está envolvida na página quando é mais provável de ser realmente usado.

Bundlers de JavaScript, como webpack, Parcel, Rollup e esbuild, podem ser configurados para dividir pacotes JavaScript em partes menores encontrar uma chamada import() dinâmica no código-fonte. A maioria dessas ferramentas faz isso automaticamente, mas o esbuild exige que você ative esse recurso otimização.

Observações úteis sobre a divisão de código

Embora a divisão de código seja um método eficaz para reduzir a contenção da linha de execução principal durante o carregamento inicial da página, vale a pena considerar algumas coisas ao decidir para auditar seu código-fonte JavaScript para identificar oportunidades de divisão de código.

Use um bundler se puder

É uma prática comum que os desenvolvedores usem módulos JavaScript durante a de desenvolvimento de software. É uma excelente melhoria da experiência do desenvolvedor que melhora a legibilidade e a manutenção do código. No entanto, existem algumas características de desempenho abaixo do ideal que podem resultar no envio de JavaScript módulos para produção.

O mais importante é usar um bundler para processar e otimizar sua fonte incluindo os módulos em que você pretende fazer a divisão. Os bundlers são muito eficazes não apenas aplicando otimizações ao código-fonte JavaScript, mas também eficaz para equilibrar questões de desempenho, como tamanho do pacote em relação à taxa de compactação. A eficácia da compressão aumenta com o tamanho do pacote, mas os bundlers também tentam garantir que os pacotes não sejam tão grandes tarefas longas devido à avaliação do script.

Os bundlers também evitam o problema de enviar um grande número de módulos desagrupados na rede. Arquiteturas que usam módulos JavaScript tendem a ter árvores de módulos complexas. Quando as árvores de módulos são desagrupadas, cada módulo representa uma uma solicitação HTTP separada, e a interatividade no seu app da Web poderá sofrer atraso se você não agrupam módulos. Embora seja possível usar Dica de recurso <link rel="modulepreload"> (link em inglês) para carregar árvores de módulos grandes logo no início possível, os pacotes JavaScript ainda são preferíveis em uma performance de carregamento de negócios.

Não desative acidentalmente a compilação de streaming

O mecanismo JavaScript V8 do Chromium oferece uma série de otimizações prontas para uso para garantir que o código JavaScript de produção seja carregado da maneira mais eficiente possível. Uma dessas otimizações é conhecida como compilação de streaming, análise incremental do HTML transmitido para o navegador—compila blocos transmitidos de JavaScript conforme eles chegam da rede.

Há algumas maneiras de garantir que a compilação de streaming ocorra para seu aplicativo da Web no Chromium:

  • Transforme seu código de produção para evitar o uso de módulos JavaScript. Agrupadores pode transformar seu código-fonte JavaScript com base em um destino de compilação e o destino geralmente é específico para um determinado ambiente. O V8 vai aplicar o streaming compilação para qualquer código JavaScript que não use módulos, e é possível configure seu bundler para transformar seu código do módulo JavaScript em uma sintaxe que não usa módulos JavaScript e seus recursos.
  • Se você quiser enviar módulos JavaScript para a produção, use o .mjs . Independentemente de o JavaScript de produção usar ou não módulos, há Nenhum tipo de conteúdo especial para JavaScript que usa módulos em vez de JavaScript. que não tem. Com relação ao V8, você desativa o streaming Compilação quando você envia módulos JavaScript em produção usando o .js. . Se você usar a extensão .mjs para módulos JavaScript, o V8 poderá garantir que a compilação de streaming para código JavaScript com base em módulo não seja de quebrar.

Não deixe que essas considerações impeçam você de usar a divisão de código. Código a divisão é uma maneira eficaz de reduzir os payloads iniciais do JavaScript para os usuários, mas usando um bundler e sabendo como preservar a transmissão do V8 comportamento de compilação, é possível garantir que o código JavaScript de produção seja o mais rápido possível para os usuários.

Demonstração de importação dinâmica

webpack

O webpack vem com um plug-in chamado SplitChunksPlugin, que permite configurar como o bundler divide os arquivos JavaScript. O webpack reconhece os dois instruções import() dinâmicas e import estáticas. O comportamento O SplitChunksPlugin pode ser modificado especificando a opção chunks no configuração:

  • chunks: async é o valor padrão e se refere a chamadas import() dinâmicas.
  • chunks: initial refere-se a chamadas import estáticas.
  • chunks: all abrange importações dinâmicas import() e estáticas, permitindo que você para compartilhar blocos entre as importações async e initial.

Por padrão, sempre que o webpack encontrar uma instrução import() dinâmica. cria um bloco separado para esse módulo:

/* main.js */

// An application-specific chunk required during the initial page load:
import myFunction from './my-function.js';

myFunction('Hello world!');

// If a specific condition is met, a separate chunk is downloaded on demand,
// rather than being bundled with the initial chunk:
if (condition) {
  // Assumes top-level await is available. More info:
  // https://v8.dev/features/top-level-await
  await import('/form-validation.js');
}

A configuração do webpack padrão para o snippet de código anterior resulta em dois blocos separados:

  • O bloco main.js, que o webpack classifica como um bloco initial, que inclui os módulos main.js e ./my-function.js.
  • O bloco async, que inclui apenas form-validation.js (contendo um hash de arquivo no nome do recurso, se configurado). Este bloco só é baixado se e quando condition for verdadeira.

Essa configuração permite adiar o carregamento do bloco form-validation.js até que quando necessário. Isso pode melhorar a capacidade de resposta de carregamento reduzindo o script de avaliação durante o carregamento inicial da página. Download e avaliação do script para o bloco form-validation.js ocorre quando uma condição especificada é atendida, Nesse caso, é feito o download do módulo importado dinamicamente. Um exemplo pode ser uma uma condição em que o download de um polyfill só é feito em um navegador específico, ou — como em exemplo anterior, o módulo importado é necessário para uma interação do usuário.

Por outro lado, mudar a configuração SplitChunksPlugin para especificar O chunks: initial garante que o código seja dividido apenas nos blocos iniciais. São blocos, como os importados estaticamente ou listados no entry do webpack propriedade. Observando o exemplo anterior, o bloco resultante seria um combinação de form-validation.js e main.js em um único arquivo de script, resultando em um desempenho potencialmente pior do carregamento da página inicial.

As opções de SplitChunksPlugin também podem ser configuradas para separar valores maiores scripts em vários menores, por exemplo, usando a opção maxSize para instruir o webpack a dividir blocos em arquivos separados se eles excederem o especificado por maxSize. Dividir arquivos de script grandes em arquivos menores pode melhorar a capacidade de resposta da carga, como em alguns casos de avaliação de script com uso intensivo da CPU. o trabalho é dividido em tarefas menores, que têm menos probabilidade de bloquear as principais por períodos mais longos.

Além disso, gerar arquivos JavaScript maiores também significa que os scripts são mais chances de invalidação de cache. Por exemplo, se você enviar um script grande com framework e código do aplicativo primário, todo o O pacote pode ser invalidado se apenas o framework for atualizado, mas nada mais o recurso empacotado.

Por outro lado, arquivos de script menores aumentam a probabilidade o visitante recupera recursos do cache, resultando em carregamentos de página mais rápidos no visitas repetidas. No entanto, arquivos menores se beneficiam menos com a compactação do que arquivos maiores e pode aumentar o tempo de retorno da rede em carregamentos de página com uma cache do navegador. É preciso ter cuidado para encontrar o equilíbrio entre o armazenamento em cache da compactação, eficácia da compactação e tempo de avaliação de script.

Demonstração do webpack

Demonstração do SplitChunksPlugin webpack.

Teste seus conhecimentos

Que tipo de instrução import é usada ao executar o código divisão?

import estático.
import() dinâmico.

Que tipo de instrução import precisa estar na parte superior. de um módulo JavaScript e em nenhum outro local?

import estático.
import() dinâmico.

Ao usar SplitChunksPlugin no webpack, qual é o diferença entre um bloco de async e um initial bloco?

async blocos são carregados usando import() dinâmico. e initial são carregados usando import.
async partes são carregadas usando import estático. e initial são carregados usando import().

A seguir: imagens de carregamento lento e elementos <iframe>

Apesar de ser um tipo de recurso bastante caro, o JavaScript não é somente o tipo de recurso que você pode adiar o carregamento. Imagem e elementos <iframe> são recursos potencialmente dispendiosos por si só. Assim como no JavaScript, pode adiar o carregamento de imagens e elementos <iframe> com o carregamento lento elas, que serão explicadas no próximo módulo do curso.