Módulos JavaScript
Um background em módulos
Os programas JavaScript começaram muito pequenos - a maior parte do seu uso nos primeiros dias era para executar tarefas isoladas de script, fornecendo um pouco de interatividade às suas páginas da Web sempre que necessário, de modo que scripts grandes geralmente não eram necessários. Com o avanço rápido da tecnologia agora temos aplicativos completos sendo executados em navegadores com muito JavaScript, além de JavaScript ser usado em outros contextos (Node.js, por exemplo).
Portanto, fez sentido nos últimos anos começar a pensar em fornecer mecanismos para dividir programas JavaScript em módulos separados que podem ser importados quando necessário. O Node.js possui essa capacidade há muito tempo e existem várias bibliotecas e estruturas JavaScript que permitem o uso do módulo (por exemplo, outros CommonJS e AMD-sistemas de módulos baseados em RequireJS, e mais recentemente Webpack e Babel).
A boa notícia é que os navegadores modernos começaram a dar suporte nativamente à funcionalidade do módulo, e é sobre isso que este artigo trata. Isso só pode ser uma coisa boa - os navegadores podem otimizar o carregamento de módulos, tornando-o mais eficiente do que ter que usar uma biblioteca e fazer todo esse processamento extra no lado do cliente e viagens de ida e volta extras.
Suporte do navegador
importa
exporta
Apresentando um exemplo
Para demonstrar o uso dos módulos, criamos um conjunto simples de exemplos que você pode encontrar no GitHub. Estes exemplos demonstram um conjunto simples de módulos que criam um em uma página da Web e desenhe (e relate informações sobre) formas diferentes na tela.
Estes são bastante triviais, mas foram mantidos deliberadamente simples para demonstrar claramente os módulos.
Nota: Nota: Se você deseja fazer o download dos exemplos e executá-los localmente, precisará executá-los por meio de um servidor da web local.
Exemplo de uma estrutura básica
No nosso primeiro exemplo (consulte basic-modules) nós temos uma estrutura de arquivos da seguinte maneira:
index.html main.js modules/ canvas.js square.js
Nota: Nota: Todos os exemplos neste guia têm basicamente a mesma estrutura; o exposto acima deve começar a ficar bem familiar.
Os dois módulos do diretório modules são descritos abaixo:
-
canvas.js
— contém funções relacionadas à configuração da tela:create()
— cria uma tela com uma largura e altura especificadas dentro de um invólucrocom um ID especificado, que é anexado dentro de um elemento pai especificado. Retorna um objeto que contém o contexto 2D da tela e o ID do wrapper.createReportList()
— cria uma lista não ordenada anexada dentro de um elemento de wrapper especificado, que pode ser usado para gerar dados de relatório. Retorna o ID da lista.square.js
— contém:name
— uma constante contendo a string 'square'.draw()
— desenha um quadrado em uma tela especificada, com um tamanho, posição e cor especificados. Retorna um objeto que contém o tamanho, a posição e a cor do quadrado.reportArea()
— grava a área de um quadrado em uma lista de relatórios específica, considerando seu tamanho.reportPerimeter()
— grava o perímetro de um quadrado em uma lista de relatórios específica, considerando seu comprimento.
Aside —
.mjs
versus.js
Neste artigo, usamos extensões .js para nossos arquivos de módulo, mas em outros recursos você pode ver a extensão .mjs usada. A documentação da V8 recomenda isso, por exemplo. Os motivos apresentados são:
- É bom para maior clareza, ou seja, deixa claro quais arquivos são módulos e quais são JavaScript regulares.
- Ele garante que seus arquivos de módulo sejam analisados como um módulo por tempos de execução, como Node.js, e construir ferramentas como Babel.
No entanto, decidimos continuar usando .js, pelo menos por enquanto. Para que os módulos funcionem corretamente em um navegador, você precisa garantir que seu servidor os esteja servindo com um cabeçalho Content-Type que contenha um tipo MIME JavaScript, como text / javascript. Caso contrário, você receberá um erro estrito de verificação do tipo MIME, de acordo com as linhas "O servidor respondeu com um tipo MIME não JavaScript" e o navegador não executará seu JavaScript. A maioria dos servidores já define o tipo correto para arquivos .js, mas ainda não para arquivos .mjs. Servidores que já veiculam arquivos .mjs incluem corretamente GitHub Pages e
http-server
para Node.js.Tudo bem se você já estiver usando esse ambiente ou se não estiver, mas souber o que está fazendo e tiver acesso (ou seja, você pode configurar o servidor para definir a configuração correta
Content-Type
para arquivos.mjs
). No entanto, isso pode causar confusão se você não controlar o servidor do qual está servindo arquivos ou publicar arquivos para uso público, como estamos aqui.Para fins de aprendizado e portabilidade, decidimos manter o
.js
.Se você realmente valoriza a clareza de usar .mjs para módulos versus usar .js para arquivos JavaScript "normais", mas não deseja se deparar com o problema descrito acima, sempre poderá usar .mjs durante o desenvolvimento e convertê-los em .js durante sua etapa de construção.
Também é importante notar que:
- Algumas ferramentas podem nunca suportar .mjs, comoTypeScript.
- O atributo
é usado para indicar quando um módulo está sendo apontado, como você verá abaixo.
Exportando recursos do módulo
A primeira coisa que você faz para obter acesso aos recursos do módulo é exportá-los. Isso é feito usando o
export
declaração.A maneira mais fácil de usá-lo é colocá-lo na frente de qualquer item que você queira exportar para fora do módulo, por exemplo:
jsexport const name = "square"; export function draw(ctx, length, x, y, color) { ctx.fillStyle = color; ctx.fillRect(x, y, length, length); return { length: length, x: x, y: y, color: color, }; }
Você pode exportar funções,
var
,let
,const
, e — como veremos mais tarde - classes. Eles precisam ser itens de nível superior; você não pode usar a exportação dentro de uma função, por exemplo.Uma maneira mais conveniente de exportar todos os itens que você deseja exportar é usar uma única instrução de exportação no final do arquivo do módulo, seguida por uma lista separada por vírgula dos recursos que você deseja exportar envoltos em chaves. Por exemplo:
jsexport { name, draw, reportArea, reportPerimeter };
Importando recursos para o seu script
Depois de exportar alguns recursos do seu módulo, é necessário importá-los para o script para poder usá-los. A maneira mais simples de fazer isso é a seguinte:
jsimport { name, draw, reportArea, reportPerimeter } from "./modules/square.js";
Você usa o
import
, seguida por uma lista separada por vírgula dos recursos que você deseja importar agrupados em chaves, seguidos pela palavra-chave de, seguida pelo caminho para o arquivo do módulo - um caminho relativo à raiz do site, que para nossabasic-modules
exemplo seria/js-examples/modules/basic-modules
.No entanto, escrevemos o caminho de maneira um pouco diferente - estamos usando a sintaxe de ponto (.) Para significar "o local atual", seguido pelo caminho além do arquivo que estamos tentando encontrar. Isso é muito melhor do que escrever todo o caminho relativo a cada vez, pois é mais curto e torna o URL portátil - o exemplo ainda funcionará se você o mover para um local diferente na hierarquia do site.
Então, por exemplo:
/js-examples/modules/basic-modules/modules/square.js
torna-se
./modules/square.js
Você pode ver essas linhas em ação em
main.js
.Nota: Nota: Em alguns sistemas de módulos, você pode omitir a extensão do arquivo e o ponto(e.g.
'/modules/square'
). Isso não funciona nos módulos JavaScript nativos.Depois de importar os recursos para o seu script, você pode usá-los exatamente como eles foram definidos no mesmo arquivo. O seguinte é encontrado em
main.js
, abaixo das linhas de importação:jslet myCanvas = create("myCanvas", document.body, 480, 320); let reportList = createReportList(myCanvas.id); let square1 = draw(myCanvas.ctx, 50, 50, 100, "blue"); reportArea(square1.length, reportList); reportPerimeter(square1.length, reportList);
Nota: Nota: Embora os recursos importados estejam disponíveis no arquivo, eles são visualizações somente leitura do recurso que foi exportado. Você não pode alterar a variável importada, mas ainda pode modificar propriedades semelhantes à const. Além disso, esses recursos são importados como ligações ativas, o que significa que eles podem mudar de valor mesmo que você não possa modificar a ligação ao contrário de const.
Aplicando o módulo ao seu HTML
Agora, apenas precisamos aplicar o módulo main.js. à nossa página HTML. Isso é muito semelhante ao modo como aplicamos um script regular a uma página, com algumas diferenças notáveis.
Primeiro de tudo, você precisa incluir
type="module"
noelemento, para declarar esse script como um módulo. Para importar o
main.js
script, usamos este:htmlVocê também pode incorporar o script do módulo diretamente no arquivo HTML, colocando o código JavaScript no corpo do elemento
O script para o qual você importa os recursos do módulo atua basicamente como o módulo de nível superior. Se você o omitir, o Firefox, por exemplo, exibirá um erro "SyntaxError: as declarações de importação podem aparecer apenas no nível superior de um módulo".
Você só pode usar
import
eexport
instruções dentro de módulos, não scripts regulares.
Outras diferenças entre módulos e scripts padrão
- Você precisa prestar atenção nos testes locais - se você tentar carregar o arquivo HTML localmente (i.e. com um arquivo
://
URL), você encontrará erros do CORS devido a requisitos de segurança do módulo JavaScript. Você precisa fazer seus testes através de um servidor. - Além disso, observe que você pode obter um comportamento diferente das seções de script definidas dentro dos módulos e não nos scripts padrão. Isso ocorre porque os módulos usam strict mode automaticamente.
- Não há necessidade de usar o atributo deferir (consulte
attributes) ao carregar um script de módulo; módulos são adiados automaticamente.
- Os módulos são executados apenas uma vez, mesmo que tenham sido referenciados em várias tags \n\n
Você também pode incorporar o script do módulo diretamente no arquivo HTML, colocando o código JavaScript no corpo do elemento \n\n
O script para o qual você importa os recursos do módulo atua basicamente como o módulo de nível superior. Se você o omitir, o Firefox, por exemplo, exibirá um erro \"SyntaxError: as declarações de importação podem aparecer apenas no nível superior de um módulo\".\n
Você só pode usar
import e
export instruções dentro de módulos, não scripts regulares."}},{"type":"prose","value":{"id":"outras_diferenças_entre_módulos_e_scripts_padrão","title":"Outras diferenças entre módulos e scripts padrão","isH3":false,"content":"
- \n
- Você precisa prestar atenção nos testes locais - se você tentar carregar o arquivo HTML localmente (i.e. com um arquivo
:// URL), você encontrará erros do CORS devido a requisitos de segurança do módulo JavaScript. Você precisa fazer seus testes através de um servidor.\n
- Além disso, observe que você pode obter um comportamento diferente das seções de script definidas dentro dos módulos e não nos scripts padrão. Isso ocorre porque os módulos usam strict mode automaticamente.\n
- Não há necessidade de usar o atributo deferir (consulte
- Além disso, observe que você pode obter um comportamento diferente das seções de script definidas dentro dos módulos e não nos scripts padrão. Isso ocorre porque os módulos usam strict mode automaticamente.\n
- Você precisa prestar atenção nos testes locais - se você tentar carregar o arquivo HTML localmente (i.e. com um arquivo