node.js 模块及模块加载
node.js 模块
在node.js开发中,一个文件就可以认为是一个独立的模块
一、node.js模块分类
1、核心模块,也叫内置模块、原生模块
例如:fs,http,path,url
所有内置模块,在安装node.js的时候,就已经编译成 二进制文件,可以直接加载运行(速度较快)
部分内置模块,在node.exe这个进程启动的时候就已经默认加载了,所以可以直接使用
2、文件模块
按文件后缀来分
如果加载时,没有指定文件后缀名,那么,就按照如下顺序依次加载相应模块
(1).js
(2).json
(3).node (C/C++编写的模块)
3、自定义模块(第三方模块)
例如:mime、moment、cheerio等等
二、require加载模块的顺序
1、看 require() 加载模块时传入的参数是否以 '/' 或 './' 或 '../' 等等这样的路径方式开头(相对路径或绝对路径都可以)
2、如果是,就会按照传入的路径直接去查询对应的模块
(1)如果传入的为具体的文件名,例如:require('./test.js')
直接根据给定的路径去加载模块,找到了就加载成功,找不到加载失败
(2)如果传入的不是具体的文件名,例如:require('./test')
第一步:根据给定的路径,依次添加文件后缀.js、.json、.node进行匹配,如果匹配不到,执行第二步
第二步:查找是否有 test 目录(尝试寻找 test 包)
找不到:加载失败
找到了:依次在test目录下查找package.json文件(找到该文件后尝试找main字段中的入口文件)、index.js、index.json、index.node,找不到则加载失败
3、不是,那就认为传入的是“模块名称”,例如:require('http')、require('mime')
是核心模块,就直接加载核心模块
不是核心模块:
依次递归查找node_modules 目录中是否有相应的包
从当前目录开始,依次递归查找所有父目录下的node_modules 目录中是否包含相应的包
如果查找完毕磁盘根目录依然没有,则加载失败
打印输出module.paths查看
三、require函数加载模块原理(被加载的模块会先执行一次)
四、require加载模块注意点:
(1)所有模块第一次加载完毕以后都会有缓存,二次加载直接读取缓存,避免了二次开销。(因为有缓存,所以模块中的代码只在第一次加载的时候执行一次)
(2)每次加载模块的时候,都优先从缓存中加载,缓存中没有的情况下,才会按照node.js加载模块的规则去查找
(3)核心模块在node.js源码编译的时候,都已经编译成二进制执行文件,所以加载速度较快,核心模块加载的优先级仅次于缓存加载
(4)核心模块都保存在在 Node.js 源代码的 lib/
目录下
(5)试图加载一个和核心模块同名的自定义模块(第三方模块)是不会成功的,因为存在同名模块的时候,require()总是会优先加载核心模块,这个情况下要使用自定义模块,要不名字不要与核心模块同名,要不就使用路径的方式加载
(6)核心模块 只能通过 模块名称来加载(错误的加载方式:require('./http');这样是无法加载 核心模块的http的)
(7)require()加载模块使用./相对路径时,相对路径是相对于当前模块,不受执行 node 命令的路径影响
(8)建议加载文件模块的时候始终添加文件的后缀名,不要省略
五、module.exports介绍
module.exports对象是由模块系统创建的,使用的时候,将期望导出的对象赋值给module.exports。
(1)module.exports用法
// a.js
function add(x, y) {
return x+y;
}
var result = add (100, 10);
console.log(result);
module.exports = 'Hello World!';
// b.js
// 一个模块,默认被require()加载后,返回的是一个对象
// require()加载,是把a.js中module.exports这个对象的值赋值给a了
var a = require('./a.js');
console.log(a);
在b.js中 require()加载的过程,自动运行a.js代码,输出了110
(2)module.exports 只能导出一个对象
// a.js
module.exports = 'Hello World!';
module.exports = '666';
// b.js
var a = require('./a.js');
console.log(a);
module.exports对象 只能导出一个对象,后面的会覆盖前面的
(3)可以一个一个设置module.exports对象的属性值
// a.js
module.exports.name = 'Tom';
module.exports.age = 26;
module.exports.show = function () {
console.log(this.name + this.age);
};
// b.js
// 一个模块,默认被require()加载后,返回的是一个对象
var a = require('./a.js');
console.log(a);
// 调用a中的方法
a.show();
(4)改变module.exports 的属性值
// a.js
module.exports = {
text:'Hello World!',
age: 12,
show: function () {
console.log(this.text + '你好');
}
};
module.exports.name = 'Tom';
module.exports.age = 26;
module.exports.show = function () {
console.log(this.name + this.age);
};
// b.js
// 一个模块,默认被require()加载后,返回的是一个对象
var a = require('./a.js');
console.log(a);
// 调用a中的方法
a.show();
六、exports和module.exports的区别
(1)exports 是 module.exports 的一个快捷方式
exports
变量是在模块的文件级别作用域内有效的,它在模块被执行前被赋予 module.exports
的值。
它有一个快捷方式,以便 module.exports.f = ...
可以被更简洁地写成 exports.f = ...
。
就是说:exports 是 module.exports 的一个快捷方式
// a.js
module.exports.name = '张三';
exports.age = 18;
exports.show = function () {
console.log(this.name + this.age);
};
// b.js
var a = require('./a.js');
console.log(a);
// 调用a中的方法
a.show();
(2)exports 和 module.exports 指向的是同一个对象
// a.js
module.exports.name = '张三';
// exports 是 module.exports 的一个快捷方式
exports.age = 18;
exports.show = function () {
console.log(this.name + this.age);
};
console.log(module.exports);
console.log(exports);
(3)如果一个新的值被赋值给 exports
,它就不再绑定到 module.exports
// a.js
module.exports.name = '张三';
exports.age = 18;
exports.show = function () {
console.log(this.name + this.age);
};
// 一个新的值被赋值给 exports,它就不再绑定到 module.exports
exports = {text: 'hello'};
exports.gender = '男';
console.log(module.exports);
console.log(exports);
// b.js
var a = require('./a.js');
console.log(a);
(4)require() 函数返回的是 module.exports 中的数据
当 module.exports 属性被一个新的对象完全替代时,最终 require() 函数返回的是 module.exports 中的数据
// a.js
module.exports.name = '张三';
exports.age = 18;
exports.show = function () {
console.log(this.name + this.age);
};
module.exports = 'Hello World!';
// b.js
var a = require('./a.js');
console.log(a);