Node.js中的module.exports和exports,在 Node.js 编程中,模块是独立的功能单元,可以在项目之间共享和重用。它们使我们作为开发人员的生活更轻松,因为我们可以使用它们来增强我们的应用程序,而无需我们自己编写的功能。它们还允许我们组织和分离我们的代码,从而使应用程序更易于理解、调试和维护。
在本文中,将研究如何使用Node.js中的模块,重点是如何导出和使用它们。
不同的Node.JS模块格式
由于JavaScript最初没有模块的概念,随着时间的推移出现了各种竞争格式。以下是需要注意的主要事项列表:
- 异步模块定义 (AMD) 格式在浏览器中使用,并使用函数来定义模块。
define
- CommonJS(CJS)格式用于Node.js并使用和定义依赖项和模块。npm 生态系统就是建立在这种格式之上的。
require
module.exports
- ES 模块 (ESM) 格式。从ES6(ES2015)开始,JavaScript支持原生模块格式。它使用关键字导出模块的公共 API,并使用关键字导入模块。
export
import
- System.register格式旨在支持ES6中的ES5模块。
- 通用模块定义 (UMD) 格式既可以在浏览器中使用,也可以在 Node.js 中使用。当模块需要由许多不同的模块加载器导入时,它很有用。
请注意,本文仅涉及 CommonJS 格式,即 Node.js 中的标准。如果你想阅读任何其他格式,我推荐这篇文章,作者是SitePoint作者Jurgen Van de Moere。
需要的模块
Node.js附带了一组内置模块,我们可以在代码中使用这些模块,而无需安装它们。要做到这一点,我们需要使用require关键字来要求模块,并将结果分配给一个变量。然后可以使用它来调用模块公开的任何方法。
例如,要列出目录的内容,可以使用文件系统模块及其readdir方法:
const fs = require('fs'); const folderPath = '/home/jim/Desktop/'; fs.readdir(folderPath, (err, files) => { files.forEach(file => { console.log(file); }); });
请注意,在 CommonJS 中,模块是同步加载的,并按其出现的顺序进行处理。
创建和导出模块
现在,让我们来看看如何创建我们自己的模块,并将其导出以在我们的程序中的其他地方使用。首先创建一个user.js文件并添加以下内容:
const getName = () => { return 'Jim'; }; exports.getName = getName;
现在在同一文件夹中创建一个index.js文件,并添加以下内容:
const user = require('./user'); console.log(`User: ${user.getName()}`);
使用运行程序node index.js
您应该会看到终端的以下输出:
User: Jim
那么这里发生了什么?好吧,如果你看看user.js
文件,您会注意到我们正在定义一个getName函数,然后使用exports
关键字使其可用于其他地方的导入。然后在.js
文件中,我们导入并执行此函数。还要注意,在require
语句中,模块名称的前缀为 ./
,因为它是本地文件。还要注意,没有必要添加文件扩展名。
导出多个方法和值
我们可以以相同的方式导出多个方法和值:
const getName = () => { return 'Jim'; }; const getLocation = () => { return 'Munich'; }; const dateOfBirth = '12.01.1982'; exports.getName = getName; exports.getLocation = getLocation; exports.dob = dateOfBirth;
而且在index.js
:
const user = require('./user'); console.log( `${user.getName()} lives in ${user.getLocation()} and was born on ${user.dob}.` );
上述代码产生了以下结果:
Jim lives in Munich and was born on 12.01.1982.
语法变化
我还应该提到,不只是在文件末尾,还可以随时随地导出方法和值。
例如:
exports.getName = () => { return 'Jim'; }; exports.getLocation = () => { return 'Munich'; }; exports.dob = '12.01.1982';
多亏了解构赋值,我们可以挑选出想要导入的内容:
const { getName, dob } = require('./user'); console.log( `${getName()} was born on ${dob}.` );
正如您所料,此日志记录:
Jim was born on 12.01.1982.
导出默认值
在上面的示例中,我们分别导出函数和值。这对于应用程序中可能需要的助手函数来说很方便,但是当你有一个只导出一个东西的模块时,使用它更常见module.exports
:
class User { constructor(name, age, email) { this.name = name; this.age = age; this.email = email; } getUserStats() { return ` Name: ${this.name} Age: ${this.age} Email: ${this.email} `; } } module.exports = User;
而且在index.js
:
const User = require('./user'); const jim = new User('Jim', 37, 'jim@example.com'); console.log(jim.getUserStats());
上述代码记录如下:
Name: Jim Age: 37 Email: jim@example.com
module.exports和exports有什么区别?
在浏览 Web 的过程中,您可能会遇到以下语法:
module.exports = { getName: () => { return 'Jim'; }, getLocation: () => { return 'Munich'; }, dob: '12.01.1982', };
这里,我们将要导出的函数和值分配给exports
上的属性module。
const { getName, dob } = require('./user'); console.log( `${getName()} was born on ${dob}.` );
这会记录以下内容:
Jim was born on 12.01.1982.
那又怎么样是两者之间的差异module.exports
和exports? 一个只是另一个的方便别名吗?
嗯,有点,但不完全…
为了说明我的意思,让我们更改中的代码index.js
记录的值 module :
console.log(module);
这将产生:
Module { id: '.', exports: {}, parent: null, filename: '/home/jim/Desktop/index.js', loaded: false, children: [], paths: [ '/home/jim/Desktop/node_modules', '/home/jim/node_modules', '/home/node_modules', '/node_modules' ] }
正如你所见,module
有一个exports属性。让我们添加一些内容:
// index.js exports.foo = 'foo'; console.log(module);
该输出:
Module { id: '.', exports: { foo: 'foo' }, ...
将属性分配给exports
还将它们添加到 module.exports这是因为(至少在最初)exports
是对的引用module.exports .
那么我应该使用哪一个?
作为module.exports
和出口
两者都指向同一个对象,通常使用哪个对象并不重要。例如:
exports.foo = 'foo'; module.exports.bar = 'bar';
此代码将导致模块的导出对象{ foo: 'foo', bar: 'bar' }
.
然而,有一个警告。分配到module.exports的内容就是从模块导出的内容。
因此,采取以下措施:
exports.foo = 'foo'; module.exports = () => { console.log('bar'); };
这只会导致导出匿名函数。这个foo
变量将被忽略
Node.js中的module.exports和exports的总结
模块已经成为JavaScript生态系统不可或缺的一部分,允许我们用较小的部分组成大型程序。我希望本文能很好地介绍如何在Node.js中使用它们,并帮助您揭开它们语法的神秘面纱。