Módulos ES6 no navegador: eles estão prontos ou não?

Você já ouviu falar sobre o uso dos módulos ES6 em um navegador? Na verdade, esses são módulos ES6 comuns. Eles se aplicam apenas ao código destinado aos navegadores.



Se alguém não sabe - é assim que parece.

Há uma página index.html:

<!DOCTYPE html>
<html>
  <body>
    <div id="app"></div>
  
    <script type="module" src="index.mjs"></script>
    <!--     "module" -->
  </body>
</html>

Há um arquivo index.mjsrepresentando um módulo conectado à página:

import { doSomething } from './utils.mjs';

doSomething('Make me a sandwich');

Este módulo, por sua vez, importa a função doSomethingdo módulo utils.mjs:

export const doSomething = message => {
  console.log('No.');
};

Os módulos ES6 existem há muitos anos. E talvez você esteja pensando em experimentá-los. Eu os usei por cerca de um mês em meu próprio projeto. Ao mesmo tempo, cheguei à conclusão de que ...

Suspeita!

Sobre o suporte do navegador para módulos


Antes de falar sobre o que entendi, sugiro dar uma olhada no suporte ao navegador para módulos. Como estou escrevendo este material no início de 2020, o nível de suporte dos módulos é impressionante em 90%.


Suporte para módulos com navegadores de acordo com caniuse.com,

estou muito satisfeito com esses 90% (não levo em consideração as necessidades de 10% dos usuários), embora você possa querer ser mais responsável. Mas, mesmo considerando isso, se o seu projeto não foi projetado para o IE, UC ou Opera Mini, esse nível de suporte significa que quase 100% do seu público-alvo poderá trabalhar com o seu projeto sem problemas.

É verdade que o suporte ao navegador para módulos é apenas o começo. Aqui, quero encontrar a resposta para três perguntas que surgiram no início de minha jornada em direção aos módulos:

  • O uso de módulos nos navegadores tem vantagens?
  • E os contras?
  • Tenho certeza de que tive uma terceira pergunta, mas agora não consigo me lembrar.

Vamos descobrir ...

Quais são as vantagens do uso de módulos nos navegadores?


Isso é puro JavaScript! Sem pipeline de projeto de montagem, sem arquivo de configuração do Webpack de 400 linhas, sem Babel, sem plugins e presets e sem módulos adicionais de 45 npm. Somente você e seu código.

Há algo refrescante na escrita de código que será executado no navegador exatamente na forma em que foi criado. Isso pode exigir um pouco mais de esforço, mas o resultado é muito agradável. Isto é como dirigir um carro com uma transmissão manual.

Assim. As vantagens dos módulos ES6 são JavaScript puro, essa é a falta de montagem, configuração do projeto e a rejeição de dependências que se tornaram desnecessárias. O quê mais? Mais alguma coisa?

Não, nada mais.

Quais são as desvantagens do uso de módulos nos navegadores?


ParComparação de módulos e empacotadores


Ao pensar em usar os módulos ES6 em um navegador, é necessário escolher entre usar módulos e bundlers como Webpack, Parcel ou Rollup.

É possível (provavelmente) usar os dois, mas, na realidade, se você planeja executar o código por meio do empacotador, não há razão para usar o design <script type="module">para carregar os arquivos do pacote.

Portanto, com base no pressuposto de que alguém está considerando a possibilidade de usar módulos ES6 em vez de um empacotador, eis o que ele terá que desistir:

  • Minificação do código finalizado.
  • Recursos avançados de JavaScript.
  • Sintaxe diferente da sintaxe JavaScript (React, TypeScript, etc.).
  • Módulos Npm.
  • Armazenamento em cache

Considere os três primeiros parágrafos desta lista com mais detalhes.

Size Tamanho do arquivo


O tamanho do meu projeto, que usa módulos, é 34 Kb. Como eu não uso a etapa de construção do projeto, o código transmitido pela rede contém nomes de variáveis ​​muito longos; há muitos comentários nele. Além disso, representa um monte de arquivos pequenos, o que não é muito bom em termos de compactação de dados.

Se eu colocasse tudo isso em um pacote usando o Parcel , o tamanho do que sairia seria 18 Kb. Minha calculadora Casual Casio relata que essa é "cerca da metade" do código do projeto no qual o empacotador não é usado. Isso se deve em parte ao fato de o Parcel compactar arquivos, mas também porque os materiais com essa abordagem são melhor compactados usando o gzip.

A compactação de dados chama nossa atenção para outro problema: a maneira como os arquivos são organizados (no sentido de conveniência do desenvolvimento) é diretamente transferida para a maneira como os arquivos são transferidos pela rede. E a maneira como os arquivos são organizados durante o desenvolvimento não corresponde necessariamente à forma como você deseja vê-los no site. Por exemplo, 150 módulos (arquivos) podem fazer sentido enquanto estiver trabalhando em um projeto. E os mesmos materiais transferidos para o navegador podem ser justificados para organizar em 12 pacotes.

Vou explicar essa ideia. Não estou falando do fato de que usar módulos significa que os arquivos não podem ser agrupados e compactados (que isso não pode ser feito é uma ilusão). Só quero dizer que não faz sentido fazer as duas coisas.

Sim, aqui está uma observação engraçada. No meu aplicativo, até escrever esta seção, foram utilizados módulos (enfatizo!). Eu instalei o Parcel para calcular o tamanho correto da montagem. E agora não vejo razão para voltar aos módulos normais. Bem, isso não é interessante!

IfeVida sem transpilação


Ao longo dos anos, estou acostumado a usar as mais recentes construções de sintaxe JavaScript e a maravilhosa ferramenta Babel, que as transforma em código que todos os navegadores entendem. Eu me acostumei tanto que raramente pensava no suporte ao navegador (é claro, com exceção do DOM e CSS).

Quando eu tentei type=«module», eu ia fazer tudo sozinho. Meu objetivo era navegadores novos, então achei que poderia usar o JavaScript moderno, ao qual estou acostumado.

Mas a realidade não era tão rósea. Tente responder às seguintes perguntas rapidamente e sem procurar em nenhum lugar. O Edge suportaflatMap()? O Safari suporta atribuição destrutiva (objetos e matrizes)? O Chrome suporta vírgulas após os argumentos da última função? O Firefox suporta exponenciação?

Por exemplo, eu tive que procurar respostas para essas perguntas, realizar testes entre navegadores. Mas eu usei tudo isso por séculos. Em qualquer aplicativo grande o suficiente, isso provavelmente levaria a erros de produção.

Isso também significa que não poderei usar a inovação JavaScript mais notável desde o advento do método array slice()- o chamado operador de sequência opcional . Eu usei desenhos mágicos comoprop?.valueaproximadamente um mês (a partir do momento em que a ferramenta Create React App começou a suportá-los sem nenhuma configuração adicional). Mas já é inconveniente para mim trabalhar sem eles.

▍ Cargas de armazenamento em cache


O armazenamento em cache foi para mim o maior obstáculo. De fato, o fato de que a solução para o problema de armazenamento em cache se mostrou bastante interessante acabou sendo a principal razão pela qual decidi escrever este material.

Como você tem certeza, quando os materiais são processados ​​usando um empacotador, cada um dos arquivos resultantes recebe um nome exclusivo - mais ou menos index.38fd9e.js. O conteúdo de um arquivo com esse nome nunca (nunca) muda. Portanto, ele pode ser armazenado em cache pelo navegador por um período ilimitado. Se esse arquivo for baixado uma vez, você não precisará baixá-lo novamente.

Este é um sistema incrível - a menos que você tente encontrar a resposta para a pergunta de como limpar o cache.

Os módulos são carregados usando um design como<script type="module" src="index.mjs"></script>. O hash no nome do arquivo não é usado. Como, nessa situação, ele deve informar o navegador sobre onde baixá-lo index.mjs- do cache ou da rede?

Deve-se notar que é quase possível fazer o cache funcionar aceitável, mas será necessário algum esforço. Aqui está o que você precisa para isso:

  • Você deve definir o título de todas as respostas cache-controlpara um valor no-cache. Incrivelmente, sem cache não significa "não armazenar em cache". Isso significa que o arquivo deve ser armazenado em cache, mas o arquivo não deve ser "usado para satisfazer uma solicitação subsequente sem verificação bem-sucedida no servidor de origem".
  • ETag. — () , , , . , 38fd9e .
  • CDN- , . ETag . . CDN- . ( ETag) . (, , Firebase).
  • -, , « » . , , , , .

Como resultado, quando um visitante visita novamente o site, o navegador diz: “Preciso fazer o download do arquivo index.mjs. Vejo que esse arquivo já existe no meu cache, é ETag 38fd9e. Solicitarei esse arquivo do servidor, mas direi a ele para enviá-lo apenas se o ETag dele não for 38fd9e. " O responsável pelo serviço interceptará essa solicitação, ignorará a ETag e a retornará index.mjsdo cache (esse arquivo entrou no cache quando a página foi carregada pela última vez). Em seguida, o responsável pelo serviço redirecionará a solicitação para o servidor. O servidor retornará uma mensagem informando que o arquivo não foi alterado ou um arquivo que será armazenado no cache.

Na minha opinião, tudo isso é muito problemático.

Agora, se você estiver interessado, o código do trabalhador do serviço:

self.addEventListener('fetch', e => {
  e.respondWith(
    (async () => {
      //       
      const cacheResponse = await caches.match(e.request);
      
      //   (),   
      // (ETag-    )
      fetch(e.request).then(fetchResponse => {
        caches
          .open(CACHE_NAME)
          .then(cache => cache.put(e.request, fetchResponse));
      });
      
      if (cacheResponse) return cacheResponse;
      
      return fetch(e.request);
    })()
  );
});

Eu estava com preguiça, então não estudei e não usei a propriedade FetchEvent.navigationPreload mais recente . O fato é que naquela época eu gastei mais tempo armazenando em cache do que escrevendo um aplicativo (para sua informação, gastei 10 e 11 horas nesses assuntos, respectivamente).

Sim, quero observar que a proposta do "cartão de importação" visa solucionar alguns dos problemas descritos acima. Ele permite que você organize algo como mapeamento index.jsem index.38fd9e.mjs. Porém, para gerar um hash, você precisa de algum tipo de pipeline de montagem, os cartões de importação precisarão ser incorporados no arquivo HTML. Isso significa que um bundler é necessário aqui ... Na verdade, nessa situação, os módulos não são mais necessários no navegador.

Como resultado, embora tenha sido interessante entender tudo isso, ele pode ser comparado com a maneira como eu dirigi em todos os lugares em um monociclo por um ano inteiro. Eu não vou mais fazer isso.

Mas provavelmente nem todo mundo usa empacotadores?


Eu escrevi este material na suposição de que todo mundo escreve código em módulos, usando construções import/exportou requiree, em seguida, coleciona o código em pacotes de produção usando Webpack, Grunt, Gulp ou algo assim.

Existem desenvolvedores que não usam empacotadores? Existe alguém que coloca seu código JavaScript em vários arquivos e os envia para produção sem agrupar? Talvez uma dessas pessoas seja você? Se assim for, eu gostaria de saber tudo sobre sua vida.

Sumário


Esforço-me para tomar todas as decisões sérias, como a escolha entre módulos e empacotadores, com base nos principais princípios do desenvolvimento efetivo. Isso é produtividade e qualidade.

Infelizmente, o uso de módulos nos navegadores não contribui para um ou outro.

Se amanhã eu começar a trabalhar em um novo projeto, então, na minha cabeça, não tenho dúvidas sobre a execução do bom e velho aplicativo create-react-app. Todas as configurações iniciais levarão cerca de trinta segundos e, embora o tamanho inicial do projeto de 40 Kb seja um pouco grande, para a maioria dos sites, isso não terá nenhum papel.

E aqui está uma situação diferente. Suponha que eu precise reunir um pouco de HTML / CSS e JavaScript para algum tipo de experimento, e esse experimento seria um projeto que incluísse um pouco mais de arquivos do que alguns. Se, enquanto trabalhava nesse projeto, não planejava gastar tempo configurando o sistema para construí-lo, provavelmente usaria os módulos no navegador. E então - se eu não me importava com o desempenho deste projeto.

Gostaria de saber como o Webpack e suas ferramentas relacionadas funcionam com os módulos ES6 em um navegador. Acredito que no futuro, quando a proposta de "cartões de importação" receber suporte suficiente, os empacotadores provavelmente os usarão como um mecanismo para abstrair hashes desagradáveis ​​que são usados ​​hoje.

Queridos leitores! Você já usou os módulos ES6 no seu navegador?


All Articles