Implementação de carregamento do módulo
- 1. Carregamento do navegador
-
- método tradicional
- regra de carga
- Diferenças entre módulos ES6 e módulos CommonJS
-
- 1. Os módulos CommonJS produzem uma cópia de um valor, enquanto os módulos ES6 produzem uma referência a um valor.
- 2. Os módulos CommonJS são carregados em tempo de execução e os módulos ES6 são interfaces de saída em tempo de compilação.
- 3. O require() do módulo CommonJS carrega o módulo de forma síncrona, e o comando import do módulo ES6 carrega de forma assíncrona, e há uma fase de análise independente das dependências do módulo.
- 3. Método de carregamento do módulo Node.js
1. Carregamento do navegador
método tradicional
Em páginas da Web em HTML, os navegadores <script>
carregam scripts JavaScript por meio de tags.
<!-- 页面内嵌的脚本 -->
<script type="application/javascript">
// module code
</script>
<!-- 外部脚本 -->
<script type="application/javascript" src="path/to/myModule.js">
</script>
Por padrão, o navegador carrega os scripts JavaScript de forma síncrona, ou seja, o mecanismo de renderização <script>
para quando encontra uma tag, aguarda até que o script seja executado e continua a renderizar. Se for um script externo, o tempo de download do script também deve ser adicionado. Se o script for grande, levará muito tempo para baixar e executar, fazendo com que o navegador seja bloqueado, e o usuário sentirá que o navegador está "travado" sem nenhuma resposta. A experiência do usuário é muito ruim.
Portanto, os navegadores permitem que os scripts sejam carregados de forma assíncrona, aqui estão duas sintaxes para carregamento assíncrono.
<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>
defer
ou async
atributo, o script será carregado de forma assíncrona. Quando o mecanismo de renderização encontra essa linha de comando, ele começa a baixar o script externo, mas não espera que ele baixe e execute, mas executa diretamente o seguinte comando.
defer
async
A diferença com:
-
defer
Ele não será executado até que toda a página seja renderizada normalmente na memória (a estrutura DOM é totalmente gerada e outros scripts são executados); -
async
Após a conclusão do download, o mecanismo de renderização interromperá a renderização e continuará a renderização após a execução deste script.
Resumir: defer
É "executar após a renderização" async
e "executar após o download". Além disso, se houver vários defer
scripts, eles serão carregados na ordem em que aparecem na página, e vários async
scripts não podem garantir a ordem de carregamento.
regra de carga
O navegador carrega ES6
o módulo, que também usa <script>
tags, mas adiciona type="module"
atributos.
<script type="module" src="./foo.js"></script>
Os módulos ES6 também permitem a incorporação em páginas da web, e a sintaxe e o comportamento são exatamente os mesmos que carregar scripts externos.
<script type="module">
import utils from "./utils.js";
// other code
</script>
Precauções:
- O código é executado no escopo do módulo, não no escopo global. Variáveis de nível superior dentro do módulo, não visíveis fora.
- Os scripts do módulo adotam automaticamente o modo estrito, declarado ou não
use strict
. - Entre os módulos, você pode usar
import
comandos para carregar outros módulos (.js
o sufixo não pode ser omitido, você precisa fornecer URLs absolutas ou relativas) e também pode usarexport
comandos para interfaces externas de saída. - Dentro de um módulo, a palavra-chave de nível superior
this
retornaundefined
, não aponta parawindow
. Em outras palavras, não faz sentido usar a palavra-chave this no nível superior do módulo. - Se o mesmo módulo for carregado várias vezes, ele será executado apenas uma vez.
Diferenças entre módulos ES6 e módulos CommonJS
1. Os módulos CommonJS produzem uma cópia de um valor, enquanto os módulos ES6 produzem uma referência a um valor.
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
};
// main.js
var mod = require('./lib');
console.log(mod.counter); // 3
mod.incCounter();
console.log(mod.counter); // 3
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
get counter() {
return counter
},
incCounter: incCounter,
};
2. Os módulos CommonJS são carregados em tempo de execução e os módulos ES6 são interfaces de saída em tempo de compilação.
3. O require() do módulo CommonJS carrega o módulo de forma síncrona, e o comando import do módulo ES6 carrega de forma assíncrona, e há uma fase de análise independente das dependências do módulo.
3. Método de carregamento do módulo Node.js
JavaScript agora tem dois tipos de módulos. Um é ES6
módulo, abreviado ESM
; o outro é CommonJS
módulo, abreviado CJS
.
CommonJS
O módulo é Node.js
proprietário e ES6
incompatível com o módulo. Acima da gramática, a diferença mais óbvia entre os dois é que CommonJS
o módulo usa require()
e module.exports
, e ES6
o módulo usa import
e export
.
Node.js
Requer que ES6
os módulos recebam .mjs
o sufixo de nome de arquivo. import
Em outras palavras, desde que o comando ou seja usado no arquivo de script export
, o nome do sufixo deve ser usado .mjs
. Quando o Node.js encontra .mjs
um arquivo, ele o considera um módulo ES6. O modo estrito é ativado por padrão e não precisa ser especificado na parte superior de cada arquivo de módulo "use strict"
.
Se você não quiser alterar o nome do sufixo para , poderá especificar o campo como no arquivo .mjs
de projeto .package.json
type
module
{
"type": "module"
}
Uma vez definidos, os scripts JS do projeto são interpretados como módulos ES6.
# 解释成 ES6 模块
$ node my-app.js
Se você quiser usar o módulo CommonJS neste momento, você precisa alterar o nome do sufixo do script CommonJS para .cjs
. Se não houver type
campo, ou type
se o campo for commonjs
, o script .js será interpretado como CommonJS
um módulo.
Resumir: .mjs
O arquivo é sempre carregado como um módulo ES6, e o arquivo é sempre carregado .cjs
como um módulo, e o carregamento do arquivo depende das configurações dos campos dentro de .CommonJS
.js
package.json
type
Módulos CommonJS carregam módulos ES6
Comandos CommonJS require()
não podem carregar módulos ES6, e um erro será reportado, então import()
este método só pode ser usado para carregá-los.
// 在 CommonJS 模块中运行
(async () => {
await import('./my-app.mjs');
})();
require()
Uma razão pela qual os módulos ES6 não são suportados é que eles são carregados de forma síncrona, e os comandos de nível superior podem ser usados dentro dos módulos ES6 await
, portanto, eles não podem ser carregados de forma síncrona.
Módulos ES6 carregam módulos CommonJS
Os comandos do módulo ES6 import
podem carregar CommonJS
módulos, mas apenas como um todo, não apenas um único item de saída.
// 正确
import packageMain from 'commonjs-package';
// 报错
import {
method } from 'commonjs-package';
Isso ocorre porque os módulos ES6 precisam oferecer suporte à análise de código estático, enquanto a interface de saída dos módulos CommonJS é module.exports
um objeto que não pode ser analisado estaticamente, portanto, só pode ser carregado como um todo.
O carregamento de um único item de saída pode ser escrito da seguinte maneira.
import packageMain from 'commonjs-package';
const {
method } = packageMain;
module.createRequire()
Há também um método de carregamento alternativo, que é usar o método interno do Node.js.
// cjs.cjs
module.exports = 'cjs';
// esm.mjs
import {
createRequire } from 'module';
const require = createRequire(import.meta.url);
const cjs = require('./cjs.cjs');
cjs === 'cjs'; // true
No código acima, o módulo ES6 module.createRequire()
pode carregar CommonJS
o módulo através do método. No entanto, esta forma de escrita é equivalente a misturar ES6
e , por isso não é recomendado o seu uso.CommonJS