Node.js中的模块分为核心模块和文件模块:
核心模块:node提供的模块,在node源码编译过程中被编译成二进制文件。node启动时,部分核心模块直接加载进内存,在路径分析中优先判断。
文件模块:用户编写的模块或是node_modules
中的第三方模块,运行时动态加载,速度比核心模块慢。
Node.js早期采用了CommonJS模块化机制作为模块加载的标准实现,但是后来随着技术演进ES6的模块化机制(也常被称作ESM,相对于CommonJS的CJS也常被称作EJS)逐渐流行起来。现在版本的Node.js核心模块对于两种模块加载机制都进行了支持,但Node.js也并没有移除CommonJS的打算,两种模块加载机制在某些特殊情况下有区别,前者的兼容性也更好,因此编写Node.js工程还是建议使用CommonJS模块规范。
CommonJS是一个JavaScript中的模块定义规范,Node.js中使用的就是这套模块加载机制,下面介绍如何引用和定义CommonJS模块。
我们使用require
可以引用一个模块。
const os = require("os");
const archStr = os.arch();
console.log(archStr);
输出:
x64
上面示例代码中,我们引用了一个叫做os
的核心模块,这个模块中的函数arch()
能够输出电脑CPU的架构。
我们可以自定义一个.js
文件作为模块,用exports
可以导出当前模块的方法或变量,exports
是module
对象的属性。引用该模块的文件通过require
引入即可。下面是一个自定义模块的例子。
myModule.js
const sum = function(a, b) {
return a + b;
};
module.exports.sum = sum;
app.js
const myModule = require("./myModule");
console.log(myModule.sum(1, 2));
注意:模块名就是模块的文件名,它需要符合小驼峰命名,引用时可以省略文件后缀名.js
,引用一个模块不必考虑变量污染,模块中的变量仅在模块内部有效。
上面代码中,我们把myModule.js
和app.js
放在了同一个目录下,因此我们使用require
引入模块时,路径使用了./myModule
,以此表示引用的模块文件和当前文件在同一个目录下,我们同样可以使用绝对路径进行模块路径的指定。如果不明确指定模块的路径,会从当前路径开始,一级一级搜索node_modules
文件夹。
例如我们的工程位于/home/ubuntu/workspace/blog
文件夹,node会按如下顺序搜索模块:
此外,node还可能会搜索NODE_PATH
,以及全局node_modules
等路径,根据使用的操作系统不同这些路径可能不同,实际开发中建议所有的第三方依赖模块都放到工程的node_modules
中,这样排查问题时能省去很多麻烦。
node会对引入过的模块进行缓存,加快模块的加载速度,核心模块和文件模块都会缓存。
node在加载模块时将模块包装成一个闭包,类似这样的:
(function (exports, require, module, __filename, __dirname) {
// module code...
});
module
即模块本身,exports
等价于module.exports
。使用require
加载模块,调用者得到module.exports
。
不过这里产生了一个问题,暴露方法时应该用module.exports
还是exports
呢。这里建议全用module.exports
,因为exports
在上面代码中只是一个形参,用它暴露属性、方法是没问题的,但是例如exports=obj
的写法就错了,这相当于在函数体内更改形参引用,是没有效果的。
除了CommonJS,Node.js也在新版本中加入了对于ES6模块化机制(即ESM)的支持。虽然目前实际开发中以CommonJS为主,但这里我们还是简单了解一下如何在Node.js工程中使用ESM模块。Node.js工程中创建ESM模块有两种方式均可以实现:
package.json
中加入配置:"type": "module"
(该值默认为commonjs
),执行该工程使用ESM模块化机制.mjs
作为后缀名(而不是.js
)下面我们使用ESM模块规范来重写之前的例子:
myModule.js
const sum = function (a, b) {
return a + b;
};
export default sum;
app.js
import sum from './myModule.js';
console.log(sum(1, 2));