Utilizando Módulos JavaScript em Produção: Situação Atual. Parte 2

Hoje publicamos a segunda parte da tradução do material, que é dedicada ao uso de módulos JS na produção.



→ A propósito, aqui está a primeira parte do artigo.

Importação dinâmica


Uma das desvantagens do uso de expressões de importação reais para separar códigos e carregar módulos é que a tarefa de trabalhar com navegadores que não suportam módulos é do desenvolvedor.

E se você deseja usar comandos import() dinâmicos para organizar o carregamento lento de código, entre outras coisas, você terá que lidar com o fato de que alguns navegadores, embora certamente suportem módulos , ainda não suportem comandos import () dinâmicos (Edge 16–18, Firefox 60–66, Safari 11, Chrome 61–63).

Felizmente, esse problema nos ajudará a resolver um polyfill pequeno (com cerca de 400 bytes) e extremamente rápido.

A adição desse polyfill a um projeto da web é muito simples. Você só precisa importá-lo e inicializá-lo no ponto de entrada principal do aplicativo (antes de chamar qualquer um dos comandos import() no código):

 import dynamicImportPolyfill from 'dynamic-import-polyfill'; //         .   //    ,       . dynamicImportPolyfill.initialize({modulePath: '/modules/'}); 

E a última coisa que precisa ser feita para que esse esquema funcione é informar ao Rollup que ele precisa renomear os comandos de import() dinâmica import() que aparecem no código usando o nome que você escolher (por meio da opção output.dynamicImportFunction ). Um polyfill que implementa o recurso de importação dinâmica usa o nome __import__ por padrão, mas pode ser personalizado .

O motivo pelo qual você precisa renomear expressões import() é porque import é, em JavaScript, uma palavra-chave. Isso significa que é impossível, por meios de polyfill, organizar a substituição do comando import() padrão por um comando com o mesmo nome. Se você tentar fazer isso, ocorrerá um erro de sintaxe.

Mas é muito bom que o Rollup renomeie os comandos durante a construção do projeto, pois isso significa que você pode usar comandos padrão no código-fonte. Além disso, no futuro, quando o polyfill não for mais necessário, o código-fonte do projeto não precisará ser reescrito, mudando para import que foi nomeado anteriormente de alguma forma diferente.

Carregamento eficiente de JavaScript


Sempre que você usa a separação de código, não é difícil organizar o pré-carregamento de todos os módulos que você sabe que serão carregados muito em breve (por exemplo, esses são todos os módulos na árvore de dependência do módulo principal, que é o ponto de entrada para o projeto).

Mas quando carregamos módulos JavaScript reais (via <script type="module"> e, em seguida, através dos comandos de import correspondentes), devemos usar o atributo modulepreload em vez da pré-carga usual, que é destinada apenas a scripts clássicos.

 <link rel="modulepreload" href="/modules/main.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-one.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-two.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-three.XXXX.mjs"> <!-- ... --> <script type="module" src="/modules/main.XXXX.mjs"></script> 

De fato, o modulepreload é definitivamente melhor do que o mecanismo tradicional de preload carregamento no pré-carregamento de módulos reais. O fato é que, quando você o usa, não é apenas o download do arquivo. Além disso, imediatamente, fora do encadeamento principal, começa a analisar e compilar o script. Um preload regular não pode fazer isso porque, durante o pré-carregamento, não sabe se o arquivo será usado como um módulo ou como um script regular.

Isso significa que carregar módulos usando o atributo modulepreload geralmente modulepreload mais rápido e, quando os módulos são inicializados, é menos provável que você crie uma carga excessiva no encadeamento principal, causando problemas de interface.

Criando uma lista de módulos para pré-carregamento


O fragmento de entrada no objeto do pacote cumulativo de rollup contém uma lista completa de importações em suas árvores de dependência estáticas. Como resultado, no gancho Rollup generateBundle , é fácil obter uma lista de arquivos que precisam ser pré-carregados.

Embora plugins possam ser encontrados no npm para gerar listas de pré-carregamento de módulos, a criação de uma lista semelhante para cada ponto de entrada na árvore de dependências requer apenas algumas linhas de código. Portanto, prefiro criar essas listas manualmente, usando algo como este código:

 {  generateBundle(options, bundle) {    //         .    const modulepreloadMap = {};    for (const [fileName, chunkInfo] of Object.entries(bundle)) {      if (chunkInfo.isEntry || chunkInfo.isDynamicEntry) {        modulepreloadMap[chunkInfo.name] = [fileName, ...chunkInfo.imports];      }    }    //  -   ...    console.log(modulepreloadMap);  } } 

Aqui, por exemplo, é como eu criei uma lista de pré - carregamento de módulos para philipwalton.com e para meu aplicativo de demonstração , sobre o qual falaremos abaixo.

Observe que, embora o atributo modulepreload definitivamente melhor do que o preload -carregamento clássico para carregar scripts do módulo, ele tem o pior suporte ao navegador (atualmente apenas o Chrome o suporta). Se uma parte significativa do seu tráfego não provém do Chrome, na sua situação, pode fazer sentido usar a preload regular em vez da preload do preload .

Em relação ao uso da preload , gostaria de avisar sobre algo. O fato é que, ao carregar scripts usando preload , diferentemente do modulepreload preload modulepreload , esses scripts não entram no mapa do módulo do navegador. Isso significa que existe a possibilidade de que as solicitações de pré-carregamento possam ser executadas mais de uma vez (por exemplo, se o módulo importa o arquivo antes que o navegador termine de pré-carregá-lo).

Por que implantar módulos reais na produção?


Se você já usa um bundler como o webpack , bem como se já usa a divisão de código e o pré-carregamento dos arquivos correspondentes (semelhante ao que acabei de dizer), talvez esteja se perguntando se deve mudar para uma estratégia focado no uso de módulos reais. Há várias razões que me fazem acreditar que você deve considerar a mudança para os módulos. Além disso, acredito que converter um projeto em módulos reais é melhor do que usar scripts clássicos com seu próprio código projetado para carregar módulos.

▍ Reduzindo a quantidade total de código


Se o projeto usar módulos reais, os usuários de navegadores modernos não precisarão baixar algum código adicional projetado para carregar módulos ou gerenciar dependências. Por exemplo, ao usar módulos reais, você não precisará carregar os mecanismos de tempo de execução e o manifesto do webpack .

▍ Código de pré-carregamento aprimorado


Conforme mencionado na seção anterior, o uso do atributo modulepreload permite carregar o código, modulepreload -lo e compilá-lo fora do encadeamento principal. Tudo o resto, comparado ao atributo preload , permanece o mesmo. Isso significa que, graças à modulepreload - modulepreload do modulepreload páginas se tornam interativas mais rapidamente e reduzem a probabilidade de bloquear o fluxo principal durante a interação do usuário.

Como resultado, independentemente do tamanho do código do aplicativo dividido em fragmentos, será muito mais produtivo fazer o download desses fragmentos usando os comandos de importação e o atributo modulepreload que carregá-los usando a tag de script usual e o atributo de preload usual (especialmente se as tags correspondentes forem geradas dinamicamente e adicionado ao DOM em tempo de execução).

Em outras palavras, um pacote cumulativo de algum código de projeto, consistindo em 20 fragmentos de módulo, carregará mais rápido que um pacote do mesmo projeto, consistindo em 20 fragmentos de script clássicos preparados pelo webpack (não por usar o webpack, mas porque que estes não são módulos reais).

▍ Melhorando o foco futuro do código


Muitos novos recursos excelentes de navegadores são criados com base em módulos e não com base em scripts clássicos. Isso significa que, se você planeja usar esses recursos, seu código deve ser apresentado na forma de módulos reais. Não deve ser algo transpilado no ES5 e carregado com os meios da tag de script clássica (esse é o problema sobre o qual escrevi quando tentei usar a API de armazenamento KV experimental).

Aqui estão alguns dos novos recursos mais interessantes do navegador que se concentram exclusivamente nos módulos:


Suporte ao navegador herdado


Globalmente, mais de 83% dos navegadores suportam módulos JavaScript (incluindo importação dinâmica), como resultado, a maioria dos usuários poderá trabalhar com um projeto que mudou para módulos sem nenhum esforço especial por parte dos desenvolvedores deste projeto.

No caso de navegadores que suportam módulos, mas não suportam importação dinâmica, é recomendável usar o polyfill dynamic-import-polyfill descrito acima . Como é muito pequeno e, se possível, usa o método import() padrão baseado em navegador, o uso desse polyfill quase não afeta o tamanho ou o desempenho do projeto.

Se falamos de navegadores que absolutamente não suportam módulos, para organizar o trabalho com eles, você pode usar o padrão módulo / nomodule .

ExampleExemplo operacional


Como é sempre mais fácil falar sobre compatibilidade entre navegadores do que alcançá-la, criei um aplicativo de demonstração que usa as tecnologias discutidas acima.

Este aplicativo funciona em navegadores como Edge 18 e Firefox ESR, que não suportam comandos de import() dinâmica import() . Além disso, ele funciona em navegadores como o Internet Explorer 11, que não suportam módulos.

Para mostrar que a estratégia discutida aqui é adequada não apenas para projetos simples, usei muitos recursos neste aplicativo que são necessários hoje em grandes projetos:

  • Transformação de código usando Babel (incluindo JSX).
  • Dependências do CommonJS (por exemplo, reagir e reagir).
  • Dependências CSS.
  • Hashing de Recursos
  • Separação de código
  • Importação dinâmica (com um fallback como um polyfill).
  • Implementação do padrão de módulo / nó.

O código do projeto pode ser encontrado no GitHub (ou seja, você pode bifurcar o repositório e construir o projeto), a versão demo está hospedada no Glitch , o que permite que você experimente.

O mais importante no projeto de demonstração é a configuração de Rollup , pois determina como os módulos resultantes são criados.

Sumário


Espero que este material tenha convencido você não apenas da possibilidade de implantar módulos JavaScript padrão na produção, mas também de que ele possa realmente melhorar o tempo de carregamento do site e seu desempenho.

Aqui está uma breve visão geral das etapas que você precisa executar para implementar os módulos no projeto:

  • Use um empacotador, entre os formatos de saída suportados pelos quais existem módulos do ES2015.
  • Abordagem agressiva da separação de código (se possível, até a alocação de dependências de node_modules em fragmentos separados).
  • Pré-carregue todos os módulos que estão na sua árvore de dependência estática (usando modulepreload ).
  • Use o polyfill para navegadores que não suportam instruções import() dinâmicas.
  • Use o padrão módulo / nomodule para organizar o trabalho com navegadores que não suportam módulos.

Se você já está usando o Rollup para criar o projeto, gostaria que você tentasse o que estava falando aqui e implante módulos reais na produção (usando técnicas de separação de código e importação dinâmica). Se o fizer, deixe-me saber como você está. Estou interessado em saber sobre problemas e sobre casos bem-sucedidos de introdução de módulos.

É muito claro que os módulos são o futuro do JavaScript. Eu gostaria de ver, e de preferência em breve, como as ferramentas que usamos e as bibliotecas auxiliares adotam essa tecnologia. Espero que este material possa pelo menos ajudar um pouco esse processo.

Caros leitores! Você usa módulos JS em produção?

Source: https://habr.com/ru/post/pt466539/


All Articles