在Node.js中,常用的模块系统是CommonJS规范,其中require
函数是加载模块的入口。这里简要介绍一下Node.js中require
的加载机制。
加载机制
Node.js中require
函数的加载机制基于以下两个原则:
1. 模块只会被加载一次,重复的调用require
只会返回内存缓存中已有的模块。
2. 模块的加载顺序是深度优先,同级模块会被加载一次,遇到已加载的模块会停止继续加载。
下面是一个常见的示例:
// a.js
console.log('a start');
require('./b');
console.log('a end');
// b.js
console.log('b start');
require('./c');
console.log('b end');
// c.js
console.log('c start');
console.log('c end');
运行node a.js
,会输出以下结果:
a start
b start
c start
c end
b end
a end
上述代码中,a.js
调用了require('./b')
,Node.js按照深度优先的原则,会首先加载b.js
。b.js
调用了require('./c')
,由于此时c.js
还没有被加载过,Node.js又会开始加载c.js
。b.js
会等待c.js
的加载完成,再执行console.log('b end')
,a.js
同理。在整个加载过程中,c.js
只被加载了一次,不论有多少模块调用了它。
示例说明
示例一
下面看一下带有exports和module.exports的模块的加载过程。
// moduleA.js
exports.a = 1;
module.exports.b = 2;
console.log('moduleA.js', module.exports);
console.log('exports === module.exports:', exports === module.exports);
// moduleB.js
const moduleA = require('./moduleA');
console.log('moduleA', moduleA);
运行node moduleB.js
,输出以下结果:
moduleA.js { a: 1, b: 2 }
exports === module.exports: true
moduleA { a: 1, b: 2 }
在moduleA.js
中,既通过exports
暴露了a
属性,也通过module.exports
暴露了b
属性,这里注意到exports
和module.exports
只是同一个对象的不同引用,引用相同的原因是后续释放只需清空一个对象即可,避免申请额外的内存和减少 V8 引擎的垃圾回收。可见通过exports和module.exports暴露的内容是合并到一个exports中的。
在moduleB.js
中,只需要加载moduleA.js
一次,因此会输出moduleA.js { a: 1, b: 2 }
。module.exports
作为require
函数加载的返回值,此处使用moduleA
接受。
示例二
模块间相互依赖
//fileA.js
var fileB = require('./fileB.js');
console.log('fileA: ', fileB.content);// undefined
//fileB.js
var fileA = require('./fileA.js');
exports.content= "Hello World";
console.log('fileB: ', fileA);// {}
//main.js
var fileA = require('./fileA.js');
console.log('main: ', fileA); // {test: 123}
运行node main.js
,输出以下结果:
fileB: {}
fileA: undefined
main: { content: 'Hello World' }
main.js
-> fileA.js
-> fileB.js
的加载顺序是深度优先,谁先执行完就先返回给调用方。在fileB.js
中require('./fileA.js')
会在fileA.js
执行中输出一个空对象,因为此时fileA
还没有完全加载并返回,所以fileB.js
中的fileA
还是空对象。
在这个示例中,因为模块出现循环依赖的情况,所以在fileA.js
中访问fileB
中的属性时,输出结果是undefined
。但是,如果是通过exports
暴露接口,而非在模块对象上动态添加接口,这种问题是不会存在的。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:简单模拟node.js中require的加载机制 - Python技术站