Node.js 模块系统入门
在编程领域中,模块是自包含的功能单元,可以跨项目共享和重用。它们使开发人员的生活更加轻松,因为我们可以使用它来增加应用程序的功能,而不必亲自编写这些功能。它还让我们可以组织和解耦代码,从而使应用程序更容易理解、调试和维护。
在本文中,我们来探究如何使用 Node.js 中的模块,主要介绍如何导出和导入。
不同的模块格式
由于JavaScript 最初没有模块的概念,随着时间的推移出现了各种相互竞争的格式。以下是主流的几种格式:
- Asynchronous Module Definition (AMD)格式, 用于浏览器端,使用
define函数定义模块。 - CommonJS (CJS) 格式,用于 Node.js,使用
require和module.exports定义依赖和模块。 - ES Module (ESM) 格式。从 ES6 (ES2015)开始,JavaScript 支持原生模块格式。它使用
export关键字导出模块的公开 API,并用import关键字导入。 - System.register 格式被设计用于在ES5 中 支持 ES6 模块。
- Universal Module Definition (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关键字使其可以在其他地方导入。然后是 index.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.
注意,我们为导出的dateOfBirth变量指定的名称可以是任何我们想要的名称(本例中为dob)。它不必与原来的变量名相同。
多种语法形式
还需要提到的是,你可以在中途导出方法和值,并不一定要在文件末尾。
例如:
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 的区别是什么
你在网上可能会遇到以下语法:
module.exports = {
getName: () => {
return 'Jim';
},
getLocation: () => {
return 'Munich';
},
dob: '12.01.1982',
};
这里我们把要导出的函数和值赋给 module 的 exports 属性——当然这也是可以的:
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 都指向同一个对象,你使用哪一个通常无关紧要。例如:
exports.foo = 'foo';
module.exports.bar = 'bar';
这段代码将会使模块的导出对象变成{ foo: 'foo', bar: 'bar' }。
不过,这里有个需要注意的地方。你赋值给module.exports的内容将成为模块导出的值。
举例如下:
exports.foo = 'foo';
module.exports = () => { console.log('bar'); };
这样只会导出一个匿名函数。foo变量将被忽略。
如果你想了解更多,我推荐你阅读 这篇文章。
总结
模块已经成为 JavaScript 生态系统不可分割的一部分,它让我们能用更小的部分组成大型程序。我希望本文为你在 Node.js 中使用模块做了良好介绍,以及帮助你了解模块语法。
作者:James Hibbard
来源:SitePoint
翻译:1024译站
更多前端技术干货尽在微信公众号:1024译站

Node.js 模块系统入门的更多相关文章
- 10、Node.js模块系统
##################################################################################介绍Node.js模块系统为了让No ...
- Node.JS模块系统
1.什么是模块? 为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统. 模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的.换言之,一个 Node.js ...
- Node.js 模块系统
为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统. 模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的.换言之,一个 Node.js 文件就是一个模块, ...
- 27.Node.js模块系统
转自:http://www.runoob.com/nodejs/nodejs-module-system.html 为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统. 模 ...
- Node.js API快速入门
Node.js API 快速入门 一.事件EventEmitter const EventEmitter = require('events'); class MyEmitter extends Ev ...
- Node.js模块
每一个Node.js都是一个Node.js模块,包括JavaScript文件(.js).JSON文本文件(.json)和二进制模块文件(.node). mymodul.js function Hell ...
- 如何发布一个自定义Node.js模块到NPM(详细步骤)
咱们闲话不多说,直接开始! 由于我从没有使用过MAC,所以我不保证本文中介绍的操作与MAC一致. 文章开始我先假定各位已经在window全局安装了Node.js,下面开始进行详细步骤介绍: 本文本着, ...
- 编写原生Node.js模块
导语:当Javascript的性能需要优化,或者需要增强Javascript能力的时候,就需要依赖native模块来实现了. 应用场景 日常工作中,我们经常需要将原生的Node.js模块做为依赖并在项 ...
- 编写原生的Node.js模块
导语:当Javascript的性能遭遇瓶颈,或者需要增强Javascript能力的时候,就需要依赖native模块来实现了. 应用场景 日常工作中,我们经常需要将原生的Node.js模块做为依赖并在项 ...
随机推荐
- oracle WHERE子句中的连接顺序
ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾. 例如: (低效,执行时间1 ...
- 自定义element树表格图标
如下图: css代码: /deep/.el-icon-arrow-right:before { content: "\e6d9"; } /deep/.el-table__expan ...
- 使用openssl 工具进行双向认证测试
1,双向认证测试(需要根证书,客户证书,服务器证书以及各自的私钥)(验证通信双方的身份) openssl s_server -accept -key certs/server.key -cert ce ...
- hdu 1255 覆盖的面积 (Bruceforce)
Problem - 1255 暴力统计覆盖超过一次的区域.1y. 代码如下: #include <cstdio> #include <cstring> #include < ...
- H3C ISDN功能组和参考点
- 手把手教你用Python实现自动特征工程
任何参与过机器学习比赛的人,都能深深体会特征工程在构建机器学习模型中的重要性,它决定了你在比赛排行榜中的位置. 特征工程具有强大的潜力,但是手动操作是个缓慢且艰巨的过程.Prateek Joshi,是 ...
- java什么是跨平台性?原理是什么?
所谓跨平台性,是指java语言编写的程序,一次编译后,可以在多个系统平台上运行. 实现原理:Java程序是通过java虚拟机在系统平台上运行的,只要该系统可以安装相应的java虚拟机,该系统就可以运行 ...
- 2018-8-10-VisualStudio-合并代码文件
title author date CreateTime categories VisualStudio 合并代码文件 lindexi 2018-08-10 19:16:52 +0800 2018-2 ...
- svn 冲突解决办法(黄色感叹号)
右键:"TortoiseSVN"->"Resolved..."已解决的..., 选中全部的文件,然后OK,,然后就可以commit提交了.
- P1067 叠罗汉
题目描述 茜茜和聪聪是关系很好的同桌,有一天,茜茜觉得天空中的白云像棉花糖一样,应该很好吃吧,但是够不着,她很烦恼. 聪聪为了帮助茜茜解决烦恼,决定帮他把天空中的白云摘下来做成棉花糖给她吃. 但是聪聪 ...