为了让文件可以相互调用,Node.js提供了一个简单的模块系统,采用CommonJS模块规范。
模块是Node.js应用程序的基本组成部分,文件和模块是一一对应的,每个文件就是一个模块。
CommonJS规范
每个文件就是一个模块,有自己的作用域。在文件中定义的变量、函数、类都是私有的,其他文件不可见。
// sum.js
let sum=0; // sum是sum.js私有变量,其他文件(模块)不可见
for (let i = 1; i <=100 ; i++) {
sum+=i;
}
console.log(sum);
每个模块内部,module代表当前模块,这个变量是一个对象,它的exports属性(module.exports)是对外的接口。加载某个模块,其实是加载该模块的exports属性。
// sum.js
let sum=0; // sum是sum.js私有变量,其他文件(模块)不可见
for (let i = 1; i <=100 ; i++) {
sum+=i;
}
console.log(sum);
module.exports.sum=sum; // 输出变量sum
特点
- 所有的代码都运行在当前模块作用域下,不会污染全局作用域
- 模块可以多次加载,但是只会在第一次加载时运行一次,缓存运行结果,以后加载,直接读取缓存结果。要想模块再次运行,必须清除缓存。
- 按照在代码中的顺序,模块依次加载。
Module对象
Node.js内部提供Module构建函数,所有的模块都是Module的实例。
function Module(id,parent)
在Node.js执行一个文件时,会给这个文件生成一个module对象和exports变量。module对象代表当前模块,另外,module对象有一个exports属性,module.exports和exports都指向同一块内存区域。
module.exports = exports = {};
module.exports属性
module.exports属性表示当前模块对外输出的接口。其他文件加载该模块,实际上就是读取的module.exports变量。
module.exports.sum=100;
module.exports.area = function (r) {
return Math.PI * r * r;
}
exports变量
Node.js为每个模块提供了一个exports变量,指向module.exports。等同于在模块的头部有一个这样的命令:var exports = module.exports;
exports.sum =100;
exports.area = function (r) {
return Math.PI * r * r;
}
require命令
require命令用于加载模块文件,如果没有会报错。
加载规则
- http、fs、path、url,内置模块,引入无路径
- ./mod或../mod,相对路径的文件模块
- /pathtomodule/mod,绝对路径文件
- mod,非原生模块的文件模块
注:
- requrie命令用于加载文件,后缀名默认为.js。
require('./test');// 等同于require('./test.js');
- 如果指定的模块文件没有发现,Node.js会尝试为文件名添加.js、.json、.node后再去搜索。
模块分类
Node.js中模块可以通过文件路径或名字获取模块的引用。模块的引用会映射到一个js文件的路径,除非它是一个内置模块。Node.js的内置模块公开了一些常用的API给开发者,并且它们在Node.js进程开始的时候就预记载了。
内置模块
Node.js的内置模块被编译成二进制形式,引用时直接使用名字而非文件路径。当第三方模块和内置模块同名时,内置模块将覆盖第三方同名模块。
let http = require('http');
第三方模块(node_modules中,npm安装)
如果模块名不是路径,也不是内置模块,Node.js将试图去当前目录的node_modules文件夹中去搜索,如果没有找到,就从父目录的node_modules中去搜索,一直到根目录。
npm命令可以方便的安装、卸载、更新node_modules目录。
let express = require('express');
文件模块
用户自己编写的模块,文件模块是按需加载的。
// 绝对路径
let test = require('/Users/hhh/Desktop/node/test.js')
// 相对路径
let test = require('./test.js')
let test = require('./test') // 等价于let test = require('./test.js');
文件目录模块
let test = require('./folder') // 等价于let test = require('./folder/index.js'); 如果不存在,则加载失败
模块加载优先级
从文件模块缓存中加载
尽管原生模块与文件模块的优先级不同,但是会优先从文件模块的缓存中加载已经存在的模块。
从原生模块中加载
原生模块的优先级仅次于文件模块缓存的优先级。require方法在解析文件名之后,优先检查模块是否在原生模块列表中。原生模块也有缓存区,优先从缓存区加载,如果缓冲区没有被加载过,则调用原生模块的加载方式进行加载和执行。
从文件中加载
当文件缓存模块中不存在,而且也不是原生模块的时候,Node.js会解析require方法传入参数,并从文件系统中加载实际文件。
参考
- http://www.runoob.com/nodejs/nodejs-module-system.html
- https://blog.csdn.net/w_q_1025/article/details/54896346
- http://javascript.ruanyifeng.com/nodejs/module.html
- https://www.cnblogs.com/littlebirdlbw/p/5670633.html