介绍了Node.js的模块设计,每一块都看成一个模块。Node.js引入模块循环的问题如何处理,模块加载规则是什么?详情请见下文。模块定义采用common.js的规则,关键字exports.a=function(){};//或module.exports={a:function(){}};模块内部变量__dirname当前模块的文件夹地址__filename当前模块的文件地址require.main执行的主入口文件//nodeapp.jsconsole.log(module.main);//appmodulerequire.resolve获取模块的绝对路径//return/path/to/node_modules/express/lib/index.jsrequire.resolve('express');exports对象模块导出对象,初始对象与module.exports模块对象的引用地址一致。在每个模块中,module的自由变量是一个指针,表示当前模块对该对象的引用。module.children当前模块导入其他模块的集合,示例如下constmoduleA=reuqire('moduleA');require.children=[moduleA];module.parent对象首先导入模块的对象,示例如下app.js//nodeapp.jsconstexpress=require('express');console.log(module.parent);//undefinedmodule.exports=express();main.jsconstapp=require('./app');//moduleMainapp.listen(8080,function(){});module.paths模块搜索路径,当前模块需要模块安装时自动加载的路径全局安装通用模块全局安装模块一般在npmconfiggetprefix+/lib/node_modules目录下,模块内部依赖安装在其自己的节点模块。例如:npminstall-gexpress/usr/local/lib/node_modules|--express|--package.json|--lib/|--node_modules|--qs|--cookie|--...carry并运行命令的模块全局安装npminstall-ggulp时,模块包含定义的gulp命令,那么它会链接到npmconfiggetprefix+/bin目录。/usr/local/bin|--gulp/usr/local/lib/node_modules|--gulp|--package.json|--bin|--gulp.js|--node_modules|--gulp-util部分安装一般模块部分安装模块和完整模块的区别在于process.cwd()+/node_modules目录,安装模块和依赖模块一般在同级目录下。./node_modules|--express|--package.json|--lib/|--qs|--cookie|--...运行命令的模块部分安装,bin目录将移动到node_modules/。bin./node_modules|--.bin|--gulp|--gulp|--package.json|--bin|--gulp.js|--gulp-util|--...加载模块机制相对路径//1.加载./a.js//2.加载./a.json//3.加载./a.node//4.加载./a/index.js//5.加载./a/index.json//6.加载./a/index.nodeconsta=require('./a');//加载上层目录constb=require('../b');绝对路径constpath=require('path');constc=require('/usr/local/lib/c');constd=require(path.resolve(__dirname,'./d'));模块名先匹配内置模块进行匹配,非内置模块使用module.paths多路径,组合prefix+/lib/node_modules加载。尝试加载fs、http、net等内置模块,加载本地模块的路径。/node_modules/express加载NODE_PATH目录中的模块$NODE_PATH/express全局目录加载$HOME/.node_modules;$HOME/.node_libraries;$PREFIX/lib/noderequire.resolve('express');模块包装器在执行模块代码之前,Node.js会用函数包装器对其进行包装,如下所示:通过这样做,Node.js实现了以下目标:它使顶级变量(用var、const或let定义)的范围限定在模块内,而不是全局对象内。它有助于提供一些看似全局的变量,实际上是特定于模块的,例如:模块和导出对象,实现者可以使用这些对象从模块中导出值快捷变量__filename和_包含模块的绝对文件名和目录路径_dirname模块循环import假设有a.js和b.js,加载a.js时导入b.js,b.js同时导入a.js,那么导入顺序是怎样的呢?实际上a.jsconsole.log('astarted');exports.done=false;constb=require('./b.js');console.log('ina,b.done=%j',b.done);exports.done=true;console.log('aends');b.jsconsole.log('bstarts');exports.done=false;consta=require('./a.js');console.log('inb,a.done=%j',a.done);exports.done=true;console.log('b结束');main.jsconsole.log('主开始');consta=require('./a.js');constb=require('./b.js');console.log('inmain,a.done=%j,b.done=%j',a.done,b.done);执行结果$nodemain.jsmainstartsastartsbstartsinb,a.done=falsebendsina,b.done=trueaendsinmain,a.done=true,b.done=true当main.js加载a.js,a.js再次加载b.js。此时b.js会尝试加载a.js。为防止无限循环,将a.js导出对象的未完成副本返回给b.js模块。然后b.js完成加载并将exports对象提供给a.js模块。参考模块-模块
