1.CommonJS规范的成因1.JavaScript没有模块系统。没有对封闭范围或依赖管理的本机支持。2.JavaScript没有标准库。除了一些核心库,没有文件系统API,没有IO流API等。3.JavaScript没有标准接口。没有WebServer、数据库等统一接口。4.JavaScript没有包管理系统。无法自动加载和安装依赖项。2、CommonJS定义一个模块非常简单,主要分为三个部分:模块引用、模块定义和模块标识。1.模块引用require('xxx')2.模块定义exports.xxxmodule.exports={}3.模块ID模块ID其实就是传递给require()方法的参数,必须是小写命名的字符串驼峰式大小写,或以.,..开头的相对路径,或绝对路径。它可以没有文件名后缀.js。3、Nodejs模块分类Node.js模块分为两类,一类是核心模块(由node提供),一类是文件模块(由用户编写)。编译Node.js源代码时,核心模块被编译成二进制可执行文件。nodejs启动过程中,部分核心模块是直接加载到内存中的,所以这部分模块导入时可以省略文件定位和编译执行这两个步骤。,所以它加载最快。另一种文件模块是动态加载的,加载速度比核心模块慢。但是Node.js把核心模块和文件模块都缓存了,所以第二次require的时候不会有重复的开销。其中,nativemodules定义在lib目录下,而filemodules则不确定。ps:核心模块分为两部分,用C/C++编写和用Javascript编写,前者在源码的src目录下,后者在lib目录下。(lib/*.js)(lib/internal部分不提供给file模块)注:可以通过process.moduleLoadList查看加载的核心模块。核心模块=原生模块4.分三步引入模块1.路径解析2.文件定位3.编译执行1.路径解析核心模块:如http,fs,path等,速度仅次于缓存.路径格式的文件:以.开头的相对路径。或..,以/开头的绝对路径。自定义模块:既不是核心模块的一部分,也不是路径形式的标识符。它是一个特殊的文件模块,可以是文件形式,也可以是包形式。此类模块的查找是所有方法中最耗时和最慢的。定位时,会给出一个可能路径的数组。没有mac电脑,直接复制阮老师的图片http://www.ruanyifeng.com/blo...关键源码https://github.com/nodejs/nod。..lib/module.js一开始就用了require,有点蒙?不用担心,这个js文件里的reuiqre和我们平时用的不一样。这里的require其实就是NativeModule.require。NativeModule的定义在https://github.com/nodejs/nod...node启动时会执行模块bootstrap_node.js,后面会分析。暂时只需要了解module.js中的require我们文件模块中使用的require不一样路径分析代码跟踪栈Module.prototype.require-->Module._load-->Module._resolveFilename-->Module._resolveLookupPaths-->Module._findPath(文件位置)-->fileName(文件绝对路径)几个方法的功能总结:Module.prototype.require:直接调用Module._load和returnModule._load:调用Module._resolveFilename获取文件的绝对路径,并添加缓存和编译模块Module._resolveFilename:获取文件的绝对路径Module._resolveLookupPaths:获取文件的可能路径Module._findPath:定位模块的绝对路径根据文件可能的路径,包括后缀补全(.js,.json,.node)等,执行进去,最后返回文件的绝对路径Module.prototype.require=function(path){assert(路径,“缺失路径”);assert(typeofpath==='string','pathmustbeastring');返回模块。',请求,parent.id);}varfilename=Module._resolveFilename(请求t,父母,是主要的);//检测缓存varcachedModule=Module._cache[filename];如果(cachedModule){updateChildren(parent,cachedModule,true);返回cachedModule.exports;}//检测是否是核心模块if(NativeModule.nonInternalExists(filename)){debug('loadnativemodule%s',request);返回NativeModule.require(文件名);}//不要调用updateChildren(),模块构造函数已经调用了。varmodule=newModule(filename,parent);//判断是否为入口模块if(isMain){process.mainModule=module;module.id='.';}//添加缓存(小步骤,因为缓存是在模块编译前进行设置的,解决循环依赖问题!!!)Module._cache[filename]=module;//编译模块tryModuleLoad(module,filename);returnmodule.exports;};Module._resolveFilename=function(request,parent,isMain){//判断是否为核心模块if(NativeModule.nonInternalExists(request)){returnrequest;}//计算所有可能的路径,对于核心模块,相对路径,绝对路径,自定义模块返回不同的数组varpaths;paths=Module._resolveLookupPaths(请求,父级,真);//查找文件名fi首先,因为那是缓存键。//计算文件的绝对路径varfilename=Module._findPath(request,paths,isMain);if(!filename){varerr=newError(`找不到模块'${request}'`);err.code='MODULE_NOT_FOUND';抛出错误;}returnfilename;};//暴露给文件模块的文件定位方法require.resolve=function(request){returnModule._resolveFilename(request,self);};//用法require.resolve('a.js')//返回/home/ruanyf/tmp/a.jsModule._resolveLookupPaths代码比较复杂,这里只是一些执行结果tt.js文件目录d/wedoctornodett.jsconsole.log(module.constructor._resolveLookupPaths('fs',module,true))console.log(module.constructor._resolveLookupPaths('/hello',module,true))console.log(module.constructor._resolveLookupPaths('../../hello',module,true))console.log(module.constructor._resolveLookupPaths('hello',module,true))1.加载核心模块时,返回null2。加载绝对路径时,return['D:\\wedoctor\\node_modules','D:\\node_modules','C:\\Users\\Xiaohan\\.node_modules','C:\\Users\\小涵\\.node_libraries','D:\\tools\\nodejs\\lib\\node']由于是绝对路径,所以会在_findPath方法中清除。3、加载相对路径时,返回['D:\\wedoctor']4。加载自定义模块时,返回['D:\\wedoctor\\node_modules','D:\\node_modules','C:\\Users\\Xiaohan\\.node_modules','C:\\Users\\小涵\\。node_libraries','D:\\tools\\nodejs\\lib\\node']上面的数组是模块所有可??能的路径。基本上,从当前路径开始,向上一层找到node_modules子目录。最后三种路径,主要是历史原因,为了保持兼容性,其实已经很少用了。Module._findPath=function(request,paths){//列出所有可能的扩展名:.js,.json,.nodevarexts=Object.keys(Module._extensions);//如果是绝对路径,则不再Searchif(request.charAt(0)==='/'){paths=[''];}//目录是否有后缀斜杠vartrailingSlash=(request.slice(-1)==='/');//第一步:如果当前路径已经在缓存中,则直接返回缓存varcacheKey=JSON.stringify({request:request,paths:paths});如果(Module._pathCache[cacheKey]){返回Module._pathCache[cacheKey];}//第二步:依次遍历所有路径for(vari=0,PL=paths.length;i
