Qual é o objetivo do Node.js module.exports e como você o utiliza?

1432

Qual é o objetivo do Node.js module.exports e como você o utiliza?

Não consigo encontrar nenhuma informação sobre isso, mas parece ser uma parte bastante importante do Node.js, como muitas vezes o vejo no código-fonte.

De acordo com a documentação Node.js :

módulo

Uma referência à corrente module. Em particular, module.exports é o mesmo que o objeto de exportações. Veja src/node.jspara mais informações.

Mas isso realmente não ajuda.

O que exatamente faz module.exportse qual seria um exemplo simples?

mrwooster
fonte

Respostas:

1590

module.exportsé o objeto que é realmente retornado como resultado de uma requirechamada.

A exportsvariável é inicialmente configurada para o mesmo objeto (ou seja, é um "apelido" abreviado)); portanto, no código do módulo, você normalmente escreveria algo como isto:

let myFunc1 = function() { ... };
let myFunc2 = function() { ... };
exports.myFunc1 = myFunc1;
exports.myFunc2 = myFunc2;

exportar (ou "expor") as funções com escopo interno myFunc1e myFunc2.

E no código de chamada você usaria:

const m = require('./mymodule');
m.myFunc1();

onde a última linha mostra como o resultado de requireé (geralmente) apenas um objeto simples cujas propriedades podem ser acessadas.

NB: se você substituir exports, ele não fará mais referência module.exports. Portanto, se você deseja atribuir um novo objeto (ou uma referência de função) exports, também deve atribuir esse novo objeto amodule.exports


Vale ressaltar que o nome adicionado ao exportsobjeto não precisa ser o mesmo que o nome do escopo interno do módulo para o valor que você está adicionando, para que você possa ter:

let myVeryLongInternalName = function() { ... };
exports.shortName = myVeryLongInternalName;
// add other objects, functions, as required

Seguido por:

const m = require('./mymodule');
m.shortName(); // invokes module.myVeryLongInternalName
Alnitak
fonte
119
Boa resposta - parece-me que 'expõe' teria sido uma melhor escolha de terminologia do que das exportações '
UpTheCreek
2
@ApopheniaOverload - você pode fazer "imports.func1, exportações.func2, etc" para ter vários métodos expostos de um arquivo.
hellatan
73
O módulo require deve ser var m = require ('./ mymodule'); , com o ponto e a barra. Dessa forma, o Node.js sabe que estamos usando um módulo local.
Gui Premonsa
7
Certifique-se de usar a sintaxe: require ('./ module_name') porque, pode haver outros módulos node.js com algum nome e, em vez de escolher seu próprio módulo, ele escolherá o que está instalado com o node.js
Sazid
3
@UpTheCreek existe uma longa tradição de se referir aos símbolos públicos expostos por um módulo como sendo 'exportados', que remontam a muitos sistemas de programação e décadas. Este não era um termo novo inventado pelos desenvolvedores do Node.
Mark Reed
218

Isso já foi respondido, mas eu gostaria de acrescentar alguns esclarecimentos ...

Você pode usar os dois exportse module.exportsimportar o código para seu aplicativo da seguinte maneira:

var mycode = require('./path/to/mycode');

O caso de uso básico que você verá (por exemplo, no código de exemplo do ExpressJS) é que você define propriedades no exportsobjeto em um arquivo .js que depois importa usandorequire()

Portanto, em um exemplo simples de contagem, você pode ter:

(counter.js):

var count = 1;

exports.increment = function() {
    count++;
};

exports.getCount = function() {
    return count;
};

... em seu aplicativo (web.js ou realmente qualquer outro arquivo .js):

var counting = require('./counter.js');

console.log(counting.getCount()); // 1
counting.increment();
console.log(counting.getCount()); // 2

Em termos simples, você pode pensar nos arquivos necessários como funções que retornam um único objeto e adicionar propriedades (cadeias, números, matrizes, funções, qualquer coisa) ao objeto retornado, configurando-os exports.

Às vezes, você deseja que o objeto retornado de uma require()chamada seja uma função que você pode chamar, em vez de apenas um objeto com propriedades. Nesse caso, você também precisa definir module.exports, assim:

(sayhello.js):

module.exports = exports = function() {
    console.log("Hello World!");
};

(app.js):

var sayHello = require('./sayhello.js');
sayHello(); // "Hello World!"

A diferença entre exportações e module.exports é explicada melhor nesta resposta aqui .

Jed Watson
fonte
como posso chamar exigir algum módulo de outra pasta que não esteja tendo a pasta raiz como a minha?
Igal
@ user301639 você pode usar caminhos relativos para percorrer a hierarquia do sistema de arquivos. requireinicia em relação à pasta em que você executa node app.js. Recomendamos que você poste uma nova pergunta com exemplos explícitos de código + estrutura da pasta para obter uma resposta mais clara.
precisa
1
Eu tive que ajustar seu exemplo module.exports para fazê-lo funcionar. arquivo: var sayHello = require('./ex6_module.js'); console.log(sayHello());e módulo:module.exports = exports = function() { return "Hello World!"; }
Jason Lydon 23/07
1
Achei o exemplo de incremento muito bom e usei isso para refrescar minha mente toda vez que sou sobrecarregado com o que estou fazendo com as exportações.
Munkee
module.exports = exports = function(){...}o segundo exportsé apenas uma variável, certo? Em outras palavras, pode sermodule.exports = abc = function()
Jeb50
60

Observe que o mecanismo do módulo NodeJS é baseado nos módulos CommonJS suportados em muitas outras implementações como RequireJS , mas também SproutCore , CouchDB , Wakanda , OrientDB , ArangoDB , RingoJS , TeaJS , SilkJS , curl.js ou até mesmo Adobe Photoshop (via PSLib ) Você pode encontrar a lista completa de implementações conhecidas aqui .

A menos que seu módulo use recursos ou módulos específicos do nó, é altamente recomendável que você o utilize, em exportsvez de module.exports que não faça parte do padrão CommonJS e, em seguida, principalmente não seja suportado por outras implementações.

Outro recurso específico do NodeJS é quando você atribui uma referência a um novo objeto em exportsvez de apenas adicionar propriedades e métodos a ele, como no último exemplo fornecido por Jed Watson neste encadeamento. Eu pessoalmente desencorajaria essa prática, pois isso quebra o suporte de referência circular do mecanismo de módulos CommonJS. Em seguida, ele não é suportado por todas as implementações e o exemplo Jed deve ser escrito desta maneira (ou semelhante) para fornecer um módulo mais universal:

(sayhello.js):

exports.run = function() {
    console.log("Hello World!");
}

(app.js):

var sayHello = require('./sayhello');
sayHello.run(); // "Hello World!"

Ou usando os recursos do ES6

(sayhello.js):

Object.assign(exports, {
    // Put all your public API here
    sayhello() {
        console.log("Hello World!");
    }
});

(app.js):

const { sayHello } = require('./sayhello');
sayHello(); // "Hello World!"

PS: Parece que o Appcelerator também implementa os módulos CommonJS, mas sem o suporte à referência circular (consulte: Appcelerator e módulos CommonJS (cache e referências circulares) )

Alexandre Morgaut
fonte
35

Algumas coisas que você deve tomar cuidado se atribuir uma referência a um novo objeto para exportse / ou modules.exports:

1. Todas as propriedades / métodos anteriormente anexados ao original exportsou module.exportssão perdidos, é claro, porque o objeto exportado agora fará referência a outro novo

Este é óbvio, mas se você adicionar um método exportado no início de um módulo existente, verifique se o objeto exportado nativo não está referenciando outro objeto no final

exports.method1 = function () {}; // exposed to the original exported object
exports.method2 = function () {}; // exposed to the original exported object

module.exports.method3 = function () {}; // exposed with method1 & method2

var otherAPI = {
    // some properties and/or methods
}

exports = otherAPI; // replace the original API (works also with module.exports)

2. No caso de um exportsou module.exportsreferenciar um novo valor, eles não fazem mais referência ao mesmo objeto

exports = function AConstructor() {}; // override the original exported object
exports.method2 = function () {}; // exposed to the new exported object

// method added to the original exports object which not exposed any more
module.exports.method3 = function () {}; 

3. Consequência complicada. Se você alterar a referência para ambos exportse module.exports, difícil dizer qual API está exposta (parece que module.exportsganha)

// override the original exported object
module.exports = function AConstructor() {};

// try to override the original exported object
// but module.exports will be exposed instead
exports = function AnotherConstructor() {}; 
Alexandre Morgaut
fonte
29

a propriedade module.exports ou o objeto exports permite que um módulo selecione o que deve ser compartilhado com o aplicativo

insira a descrição da imagem aqui

Eu tenho um vídeo sobre module_export disponível aqui

anish
fonte
18

Ao dividir o código do programa em vários arquivos, module.exportsé usado para publicar variáveis ​​e funções para o consumidor de um módulo. A require()chamada no seu arquivo de origem é substituída pela correspondente module.exportscarregada do módulo.

Lembre-se de escrever módulos

  • As cargas do módulo são armazenadas em cache, apenas a chamada inicial avalia o JavaScript.
  • É possível usar variáveis ​​e funções locais dentro de um módulo, nem tudo precisa ser exportado.
  • O module.exportsobjeto também está disponível como exportsabreviação. Mas, ao retornar uma única função, sempre use module.exports.

diagrama de exportação do módulo

De acordo com: "Módulos Parte 2 - Escrevendo módulos" .

pspi
fonte
9

o link de referência é assim:

exports = module.exports = function(){
    //....
}

as propriedades de exportsou module.exports, como funções ou variáveis, serão expostas fora

há algo que você deve prestar mais atenção: não overrideexporte.

porque ?

porque exporta apenas a referência de module.exports, você pode adicionar as propriedades às exportações, mas se você substituir as exportações, o link de referência será quebrado.

bom exemplo :

exports.name = 'william';

exports.getName = function(){
   console.log(this.name);
}

mau exemplo :

exports = 'william';

exports = function(){
     //...
}

Se você deseja apenas expor apenas uma função ou variável, assim:

// test.js
var name = 'william';

module.exports = function(){
    console.log(name);
}   

// index.js
var test = require('./test');
test();

este módulo expôs apenas uma função e a propriedade do nome é privada para o exterior.

qianjiahao
fonte
6

Existem alguns módulos padrão ou existentes no node.js. quando você baixa e instala o node.js. como http, sys etc.

Como eles já estão no node.js, quando queremos usar esses módulos, basicamente gostamos dos módulos de importação , mas por quê? porque eles já estão presentes no node.js. Importar é como tirá-los do node.js e colocá-los em seu programa. E depois usá-los.

Enquanto Exports é exatamente o oposto, você está criando o módulo que deseja, digamos o módulo additional.js e colocando esse módulo no node.js, você o faz exportando-o.

Antes de escrever qualquer coisa aqui, lembre-se, module.exports.additionTwo é o mesmo que imports.additionTwo

Huh, então essa é a razão, gostamos

exports.additionTwo = function(x)
{return x+2;};

Tenha cuidado com o caminho

Digamos que você criou um módulo add.js,

exports.additionTwo = function(x){
return x + 2;
};

Quando você executa isso no prompt de comando NODE.JS:

node
var run = require('addition.js');

Isto irá errar ao dizer

Erro: Não foi possível encontrar o módulo additional.js

Isso ocorre porque o processo node.js não pode adicionar o.js, pois não mencionamos o caminho. Portanto, podemos definir o caminho usando NODE_PATH

set NODE_PATH = path/to/your/additon.js

Agora, isso deve ser executado com sucesso, sem erros!

Mais uma coisa, você também pode executar o arquivo additional.js não configurando o NODE_PATH, de volta ao prompt de comando do nodejs:

node
var run = require('./addition.js');

Como estamos fornecendo o caminho aqui, dizendo que ele está no diretório atual, ./isso também deve ser executado com êxito.

JumpMan
fonte
1
é exportação ou exportação?
rudrasiva86
Obrigado pela ajuda :)
JumpMan
3

Um módulo encapsula o código relacionado em uma única unidade de código. Ao criar um módulo, isso pode ser interpretado como mover todas as funções relacionadas para um arquivo.

Suponha que exista um arquivo Hello.js que inclua duas funções

sayHelloInEnglish = function() {
  return "Hello";
};
sayHelloInSpanish = function() {
  return "Hola";
};

Nós escrevemos uma função apenas quando a utilidade do código é mais de uma chamada.

Suponha que desejemos aumentar a utilidade da função para um arquivo diferente, como World.js, neste caso, exportar um arquivo entra em cena que pode ser obtido pelo module.exports.

Você pode apenas exportar ambas as funções pelo código fornecido abaixo

var anyVariable={
 sayHelloInEnglish = function() {
      return "Hello";
    };
  sayHelloInSpanish = function() {
      return "Hola";
    }; 
}
module.export=anyVariable;

Agora você só precisa solicitar o nome do arquivo no World.js para usar essas funções

var world= require("./hello.js");
Shantanu Madane
fonte
Graças Se ele tem ajudado você queira aceitar minha resposta :)
Shantanu Madane
1
Um pouco tarde para o pal partido :)
Ben Taliadoros
@ BenTaliadoros Eu também acho que ele está atrasado e também acho que seu objeto anyVariable tem muitos erros. a linha acima do método sayHelloInSpanish não deve terminar com ponto-e-vírgula (;) e a função sayHelloInSpanish = está incorreta. Todas as coisas estão erradas com esse objeto. i irá editar sua resposta
divina
1
a edição está desativada. O que mais o alphadogg editou nesta resposta?
divina
Apenas formatação. A menos que seja algo es6 maluco que eu não tenha encontrado, e tenho certeza de que não é, então não é JS válido
Ben Taliadoros
2

A intenção é:

A programação modular é uma técnica de design de software que enfatiza a separação da funcionalidade de um programa em módulos independentes e intercambiáveis, de modo que cada um contenha tudo o necessário para executar apenas um aspecto da funcionalidade desejada.

Wikipedia

Eu imagino que fica difícil escrever programas grandes sem código modular / reutilizável. No nodejs, podemos criar programas modulares utilizando a module.exportsdefinição do que expomos e compomos nosso programa require.

Tente este exemplo:

fileLog.js

function log(string) { require('fs').appendFileSync('log.txt',string); }

module.exports = log;

stdoutLog.js

function log(string) { console.log(string); }

module.exports = log;

program.js

const log = require('./stdoutLog.js')

log('hello world!');

executar

$ node program.js

Olá Mundo!

Agora tente trocar ./stdoutLog.js por ./fileLog.js .

Moriarty
fonte
1

Qual é o objetivo de um sistema de módulos?

Ele realiza o seguinte:

  1. Mantém nossos arquivos inchados a tamanhos realmente grandes. Ter arquivos com, por exemplo, 5000 linhas de código, geralmente é muito difícil de lidar durante o desenvolvimento.
  2. Impõe a separação de preocupações. Dividir nosso código em vários arquivos nos permite ter nomes de arquivos apropriados para cada arquivo. Dessa forma, podemos identificar facilmente o que cada módulo faz e onde encontrá-lo (assumindo que criamos uma estrutura de diretórios lógicos que ainda é de sua responsabilidade).

Ter módulos facilita a localização de certas partes do código, o que torna o nosso código mais sustentável.

Como funciona?

NodejS usa o sistema de módulo CommomJS que funciona da seguinte maneira:

  1. Se um arquivo deseja exportar algo, ele deve ser declarado usando module.exportsintaxe
  2. Se um arquivo deseja importar algo, ele deve ser declarado usando require('file')sintaxe

Exemplo:

test1.js

const test2 = require('./test2');    // returns the module.exports object of a file

test2.Func1(); // logs func1
test2.Func2(); // logs func2

test2.js

module.exports.Func1 = () => {console.log('func1')};

exports.Func2 = () => {console.log('func2')};

Outras coisas úteis a saber:

  1. Os módulos estão sendo armazenados em cache . Quando você está carregando o mesmo módulo em 2 arquivos diferentes, o módulo precisa ser carregado apenas uma vez. Na segunda vez em que a require()é chamada no mesmo módulo, ela é retirada do cache.
  2. Os módulos são carregados em síncrono . Esse comportamento é necessário, se fosse assíncrono, não poderíamos acessar o objeto recuperado require()imediatamente.
Willem van der Veen
fonte
-3
let test = function() {
    return "Hello world"
};
exports.test = test;
Gtm
fonte
4
Este é um exemplo semelhante ao do primeiro trecho na resposta aceita ( return "Hello world"não faz diferença), mas sem nenhuma explicação. Antes de responder, certifique-se de que sua resposta adicione algo ao assunto.
barbsan