Node.js module.exports的用途是什么,如何使用它?


1432

Node.js module.exports的用途是什么,如何使用它?

我似乎找不到任何相关信息,但是正如我在源代码中经常看到的那样,它似乎是Node.js的重要组成部分。

根据Node.js文档

模组

对当前的引用 module。特别module.exports 是与导出对象相同。请参阅 src/node.js以获取更多信息。

但这并没有真正的帮助。

究竟是module.exports做什么的,一个简单的例子是什么?

Answers:


1590

module.exportsrequire调用结果实际返回的对象。

exports变量最初设置为同一个对象(即它是一个速记“别名”),所以在模块的代码,您通常会写是这样的:

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

导出(或“暴露”)内部作用域函数myFunc1myFunc2

在调用代码中,您将使用:

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

最后一行显示的结果require(通常)只是一个可以访问其属性的普通对象。

注意:如果您覆盖exports,则将不再引用module.exports。因此,如果您希望将新对象(或函数引用)exports分配给该对象,则还应将该新对象分配给module.exports


值得注意的是,添加到exports对象的名称不必与要添加的值的模块内部作用域名称相同,因此您可以:

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

其次是:

const m = require('./mymodule');
m.shortName(); // invokes module.myVeryLongInternalName

119
好的答案-在我看来,“暴露”比“出口”是更好的术语选择
UpTheCreek 2012年

2
@ApopheniaOverload-您可以执行“ exports.func1,exports.func2等”,以从一个文件中获取多种公开方法。
hellatan

73
模块require应该是var m = require('./ mymodule'); ,带点和斜线。这样,Node.js就知道我们正在使用本地模块。
Gui Premonsa 2012年

7
确保使用:require('./ module_name')语法,因为可能还有其他一些具有相同名称的node.js模块,而不是选择您自己的模块,而是会选择与node.js一起安装的模块。
2013年

3
@UpTheCreek有一个悠久的传统,即将模块公开的公共符号称为“导出”,其历史可追溯到许多编程系统和数十年的历史。这不是Node开发人员发明的新术语。
Mark Reed

218

这已经得到回答,但是我想补充一些澄清...

您可以同时使用exportsmodule.exports将代码导入应用程序,如下所示:

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

您将看到的基本用例(例如,在ExpressJS示例代码中)是exports在.js文件中的对象上设置属性,然后使用require()

因此,在一个简单的计数示例中,您可以拥有:

(counter.js):

var count = 1;

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

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

...然后在您的应用程序(web.js或其他任何.js文件)中:

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

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

简而言之,您可以将所需文件视为返回单个对象的函数,并且可以通过将其设置为,将属性(字符串,数字,数组,函数,任何东西)添加到返回的对象中exports

有时,您希望从require()调用返回的对象是可以调用的函数,而不仅仅是具有属性的对象。在这种情况下,您还需要这样设置module.exports

(sayhello.js):

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

(app.js):

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

此答案中,可以更好地解释export和module.exports之间的区别。


如何调用其他文件夹中没有我的某些根文件夹的模块?
伊加尔

@ user301639,可以使用相对路径遍历文件系统层次结构。require相对于您node app.js在其中执行的文件夹开始。我建议您发布一个包含显式代码+文件夹结构示例的新问题,以获得更清晰的答案。
杰德·沃森

1
我不得不调整您的module.exports示例以使其正常工作。文件:var sayHello = require('./ex6_module.js'); console.log(sayHello());和模块:module.exports = exports = function() { return "Hello World!"; }
Jason Lydon 2014年

1
发现增量示例确实非常好,每当我对出口所做的工作不胜枚举时,我就用它来刷新思路。
munkee 2014年

module.exports = exports = function(){...}第二个exports只是一个变量吧?换句话说,可能是module.exports = abc = function()
Jeb50

60

请注意,NodeJS模块机制基于CommonJS模块,该模块在RequireJS等许多其他实现中也受支持,还包括SproutCoreCouchDBWakandaOrientDBArangoDBRingoJSTeaJSSilkJScurl.js甚至Adobe Photoshop(通过PSLib)。您可以在此处找到已知实现的完整列表。

除非您的模块使用特定于节点的功能或模块,否则我强烈建议您使用exports而不是module.exports CommonJS标准的一部分,而其他大多数实现都不支持。

NodeJS的另一个特定功能是,当您分配对新对象的引用时,exports而不仅仅是像Jed Watson在该线程中提供的最后一个示例那样向其添加属性和方法。我个人不赞成这种做法,因为这破坏了CommonJS模块机制的循环引用支持。然后,并非所有实现都支持它,然后应以这种方式(或类似方式)编写Jed示例以提供更通用的模块:

(sayhello.js):

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

(app.js):

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

或使用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:看来Appcelerator也实现了CommonJS模块,但是没有循环引用支持(请参阅:Appcelerator和CommonJS模块(缓存和循环引用)


35

如果您将对新对象的引用分配给exports和/或,则必须注意一些事项modules.exports

1.先前附加到原始属性的所有属性/方法,exports或者module.exports当然会丢失,因为导出的对象现在将引用另一个新属性/方法

这很明显,但是如果您在现有模块的开头添加导出的方法,请确保本机导出的对象在末尾没有引用另一个对象

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.如果一个exportsmodule.exports引用一个新值,则它们不再引用同一对象

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.棘手的结果。如果您同时更改对exports和的引用module.exports,则很难说公开了哪个API(看起来很成功module.exports)。

// 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() {}; 

29

module.exports属性或exports对象允许模块选择应与应用程序共享的内容

在此处输入图片说明

我在此处有关于module_export的视频


18

将程序代码划分为多个文件时,module.exports用于将变量和函数发布给模块的使用者。require()源文件中的调用将替换为module.exports从模块中加载的相应文件。

在编写模块时记住

  • 模块加载被缓存,只有初始调用评估JavaScript。
  • 可以在模块内使用局部变量和函数,而不需要导出所有内容。
  • module.exports对象也可用作exports速记。但是,当返回唯一函数时,请始终使用module.exports

模块出口图

根据:“模块第2部分-编写模块”


9

引用链接是这样的:

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

exports或的属性(module.exports例如函数或变量)将暴露在外部

需要注意的一点:不要override出口。

为什么呢?

因为导出仅是module.exports的引用,所以可以将属性添加到导出中,但是如果覆盖导出,则引用链接将断开。

好的例子 :

exports.name = 'william';

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

不好的例子:

exports = 'william';

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

如果您只想公开一个函数或变量,例如:

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

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

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

此模块仅公开一个功能,并且name的属性是外部专有的。


6

当您下载并安装node.js时,例如http,sys等,node.js中有一些默认或现有模块。

因为它们已经在node.js中,所以当我们想使用这些模块时,我们基本上喜欢导入模块,但是为什么呢?因为它们已经存在于node.js中。导入就像将它们从node.js中取出并放入程序中一样。然后使用它们。

尽管Exports恰好相反,但是您正在创建所需的模块,例如将模块additional.js放入该节点中,然后通过导出将该模块放入node.js中。

之前,我在这里写东西,记住,module.exports.additionTwo是一样exports.additionTwo

嗯,这就是原因,我们确实喜欢

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

小心路径

假设您已经创建了一个additional.js模块,

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

在NODE.JS命令提示符下运行此命令时:

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

这会出错说

错误:找不到模块additioning.js

这是因为因为我们没有提到路径,所以node.js进程无法添加。因此,我们可以使用NODE_PATH设置路径

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

现在,这应该可以成功运行,没有任何错误!!

还有一件事,您也可以通过不设置NODE_PATH来运行additional.js文件,回到您的nodejs命令提示符:

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

由于我们通过说它在当前目录中来提供路径,因此它./也应该成功运行。


1
是出口还是出口?
rudrasiva86 '17

感谢您的帮助:)
JumpMan

3

模块将相关代码封装到单个代码单元中。创建模块时,这可以解释为将所有相关功能移动到文件中。

假设有一个文件Hello.js,其中包含两个函数

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

我们仅在代码的效用超过一个调用时才编写函数。

假设我们想将该功能的实用性增加到另一个文件,例如World.js,在这种情况下,导出文件就可以通过module.exports获取。

您可以通过下面给出的代码导出两个函数

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

现在,您只需要在World.js中输入文件名即可使用这些功能

var world= require("./hello.js");

谢谢如果它对您有帮助,请接受我的回答:)
Shantanu Madane

1
对党友来说有点晚了:)
Ben Taliadoros

@BenTaliadoros我也认为他来晚了,我也认为他的anyVariable对象有很多错误。sayHelloInSpanish方法上方的行不应以分号(;)结尾,并且sayHelloInSpanish =函数错误。这个对象万事大吉。我将编辑他的答案
神2015年

1
编辑被禁用。alphadogg在此答案中还编辑了什么?
神的

只是格式化。除非我没有遇到过一些疯狂的es6事情,并且我确定不是,否则它根本就不是有效的JS
Ben Taliadoros

2

目的是:

模块化编程是一种软件设计技术,它强调将程序的功能分为独立的,可互换的模块,以使每个模块都包含执行所需功能的一个方面所必需的所有内容。

维基百科

我想如果没有模块化/可重用的代码就很难编写大型程序。在nodejs中,我们可以使用module.exports定义暴露和组成程序的方式来创建模块化程序require

试试这个例子:

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;

程式

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

log('hello world!');

执行

$节点program.js

你好,世界!

现在尝试将./stdoutLog.js交换为./fileLog.js


1

模块系统的目的是什么?

它完成以下任务:

  1. 防止文件膨胀到很大的尺寸。在开发过程中,通常很难处理包含5000行代码的文件。
  2. 加强关注点分离。将我们的代码分成多个文件,使我们可以为每个文件使用适当的文件名。这样,我们可以轻松地确定每个模块的功能以及在哪里找到它(假设我们已经制定了逻辑目录结构,这仍然是您的责任)。

拥有模块可以更轻松地查找代码的某些部分,从而使我们的代码更具可维护性。

它是如何工作的?

NodejS 使用以以下方式工作的CommomJS模块系统:

  1. 如果文件要导出某些内容,则必须使用module.export语法对其进行声明
  2. 如果文件要导入某些内容,则必须使用require('file')语法对其进行声明

例:

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')};

其他有用的事情要知道:

  1. 模块正在被缓存。当您在2个不同的文件中加载同一模块时,该模块仅需加载一次。第二次require()在同一模块上调用a时,将其从缓存中拉出。
  2. 模块以同步方式加载。此行为是必需的,如果它是异步的,我们将无法立即访问从该对象检索的对象require()

-3
let test = function() {
    return "Hello world"
};
exports.test = test;

4
这与接受的答案中的第一个片段类似(return "Hello world"没有区别),但没有任何解释。在回答之前,请确保您的答案会为主题添加一些内容。
barbsan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.