Quase todos os desenvolvedores do Node.js podem dizer o que o `requer ()` função faz, mas quantos de nós realmente sabem como funciona? Nós o usamos todos os dias para carregar bibliotecas e módulos, mas seu comportamento é um mistério para nós.
Por curiosidade, mergulhei no código principal do nó para descobrir o que aconteceu sob o motor. Mas esta não é uma única função. Encontrei o Module.js no sistema do módulo do nó. O arquivo contém um módulo principal surpreendentemente poderoso e relativamente desconhecido que controla a carga, compilação e armazenamento em cache de cada arquivo. `requer ()`, seu emergência é apenas a ponta do iceberg.
Module.js
A cópia do código é a seguinte:
Módulo de função (id, pai) {
this.id = id;
this.exports = {};
this.parent = pai;
// ...
No Module.js, ele desempenha principalmente dois papéis dentro do Node.js. Primeiro, ele fornece uma base para todos os módulos Node.js. Cada arquivo é uma nova instância do módulo base novo e ainda existe mesmo após a execução do arquivo. É por isso que somos capazes de anexar as propriedades ao módulo.Exports e devolvê -las quando necessário.
A segunda tarefa principal deste módulo é lidar com o mecanismo de carregamento do módulo do nó. A operação independente da função "requisição" que usamos é na verdade um módulo de conceito abstrato. Esse método de carga lida com o carregamento real de cada arquivo e inicia nossa jornada até lá.
Module._load
A cópia do código é a seguinte:
Module._load = function (solicitação, pai, ismain) {
// 1. Verifique o módulo._cache para o módulo em cache.
// 2. Crie uma nova instância do módulo se o cache estiver vazio.
// 3. Salve -o no cache.
// 4. Call Module.load () com o nome do arquivo fornecido.
// Isso chamará o Module.compile () depois de ler o conteúdo do arquivo.
// 5. Se houve um erro de carregamento/análise do arquivo,
// Exclua o módulo ruim do cache
// 6. Módulo de retorno.Exports
};
Module._load é responsável por carregar novos módulos e gerenciar caches do módulo. O cache carregou cada módulo reduz o número de leituras de arquivos redundantes e pode acelerar significativamente seu aplicativo. Além disso, a instância do módulo compartilhado permite que os módulos com características de singleton permaneçam no estado do projeto.
Se um módulo não existir no cache, o Module._load criará um novo módulo base para o arquivo. Em seguida, ele dirá ao módulo ler o conteúdo do novo arquivo antes de enviá -los para o Module._compile. [1]
Se você perceber a etapa 6 acima, verá que o Module.Exports foi devolvido ao usuário. É por isso que quando você está definindo o uso de interface pública, usa exportações e module.exports, porque o módulo._load retornará em seguida o conteúdo do requisito. Fiquei surpreso que não havia mais recursos aqui, mas seria melhor se houvesse.
Module._compile
A cópia do código é a seguinte:
Module.prototype._compile = function (content, nome do arquivo) {
// 1. Crie o autônomo requer função que chama o Module.Require.
// 2. Anexe outros métodos auxiliares a serem necessários.
// 3. Envolve o código JS em uma função que fornece nossa necessidade,
// módulo, etc. Variáveis localmente para o escopo do módulo.
// 4. Execute essa função
};
・ É aqui que o verdadeiro milagre acontece. Primeiro, uma função operacional especial e independente é criada para o módulo. Este é um recurso que precisamos e todos conhecem. A função em si é apenas um encapsulamento no Module.Require, que também contém alguns métodos auxiliares menos conhecidos que são fáceis de usar:
・ Requer (): carregar um módulo externo
・ Requer.Resolve (): analise um nome de módulo em seu caminho absoluto
・ Requer.Main: Módulo Principal
・ Requer.cache: todos os módulos em cache
・ ・ Requer. Extensões: um método de compilação que pode ser usado para cada tipo de arquivo válido de acordo com sua extensão
Depois que a necessidade estiver pronta, todo o código -fonte carregado será encapsulado em uma nova função, permitindo que ele aceite exigir, módulo, exportação e todas as outras variáveis expostas como parâmetros. Esta é uma função criada apenas para encapsular os módulos para evitar conflitos com o ambiente Node.js.
A cópia do código é a seguinte:
(função (exporta, requer, módulo, __filename, __dirname) {
// Seu código injetado aqui!
});
O método Module._compile é executado de maneira síncrona, portanto, a chamada para o Module._load só pode esperar até que o código seja executado termine e retorna o módulo.Exprts ao usuário.
para concluir
Portanto, já entendemos o código completo de exigir e temos uma compreensão preliminar de como ele funciona.
Se você fez isso o tempo todo, estará pronto para o último segredo: requer ('módulo'). Isso está correto, o próprio sistema de módulos pode ser carregado através do sistema de módulos. Começo. Isso pode parecer estranho, mas permite que o espaço do usuário interaja com os sistemas de carregamento do módulo sem se aprofundar no núcleo node.js. Os módulos populares são todos construídos assim. [2]
Se você quiser saber mais, verifique o código -fonte do Module.js. Ainda há muitas coisas que serão suficientes para você ter dor de cabeça por um tempo. O primeiro pode me dizer o que node_module_contexts é "e por que é adicionado pode obter pontos de bônus :)
[1] O método do módulo._compile é usado apenas para executar arquivos JavaScript. O arquivo JSON precisa ser analisado através do JSON.PARSE () e devolvido
[2] No entanto, ambos os módulos são construídos em métodos de módulo privado, como módulo._resolvelookuppaths e Module._findPath. Você pode pensar que isso não é muito melhor ...