node.js的模块加载功能主要是在module.js文件里面实现的。从var routes = require('./routes/index');这个不陌生的方法开始分析。
Module.prototype.require = function(path) { assert(typeof path === 'string', 'path must be a string'); assert(path, 'missing path'); return Module._load(path, this); };
_load:
Module._load = function(request, parent, isMain) { if (parent) { debug('Module._load REQUEST ' + (request) + ' parent: ' + parent.id); } var filename = Module._resolveFilename(request, parent); var cachedModule = Module._cache[filename]; if (cachedModule) { return cachedModule.exports; } if (NativeModule.exists(filename)) { // REPL is a special case, because it needs the real require. if (filename == 'repl') { var replModule = new Module('repl'); replModule._compile(NativeModule.getSource('repl'), 'repl.js'); NativeModule._cache.repl = replModule; return replModule.exports; } debug('load native module ' + request); return NativeModule.require(filename); } var module = new Module(filename, parent); if (isMain) { process.mainModule = module; module.id = '.'; } Module._cache[filename] = module; var hadException = true; try { module.load(filename); hadException = false; } finally { if (hadException) { delete Module._cache[filename]; } } return module.exports; };
这个方法跟NativeModule实现的一样,加了个缓存。module.load(filename);这个是他们之前的不同,也是重点。
load:不用多说,API文档里面的伪代码已经说的很清楚,模块加载机制的伪代码:
require(X) from module at path Y 1. If X is a core module, // 1.核心模块 a. return the core module b. STOP 2. If X begins with './' or '/' or '../' // 2.目录结构 a. LOAD_AS_FILE(Y + X) b. LOAD_AS_DIRECTORY(Y + X) 3. LOAD_NODE_MODULES(X, dirname(Y)) 4. THROW "not found" LOAD_AS_FILE(X) // 3.直接加载模块 1. If X is a file, load X as JavaScript text. STOP 2. If X.js is a file, load X.js as JavaScript text. STOP 3. If X.node is a file, load X.node as binary addon. STOP LOAD_AS_DIRECTORY(X) 1. If X/package.json is a file, // 4.通过X下面的package.json文件加载 a. Parse X/package.json, and look for "main" field. b. let M = X + (json main field) c. LOAD_AS_FILE(M) 2. If X/index.js is a file, load X/index.js as JavaScript text. STOP // 5.通过X目录下的index.js文件加载 3. If X/index.node is a file, load X/index.node as binary addon. STOP // 6.通过X目录下的index.node文件加载 LOAD_NODE_MODULES(X, START) 1. let DIRS=NODE_MODULES_PATHS(START) 2. for each DIR in DIRS: a. LOAD_AS_FILE(DIR/X) b. LOAD_AS_DIRECTORY(DIR/X) NODE_MODULES_PATHS(START) 1. let PARTS = path split(START) 2. let ROOT = index of first instance of "node_modules" in PARTS, or 0 3. let I = count of PARTS - 1 4. let DIRS = [] 5. while I > ROOT, a. if PARTS[I] = "node_modules" CONTINUE c. DIR = path join(PARTS[0 .. I] + "node_modules") b. DIRS = DIRS + DIR c. let I = I - 1 6. return DIRS
node.js的模块类型:核心模块(二进制),第三方模块主要是以.js为主,也有.node的用C++扩展的。
模块加载:
核心模块进程启动的时候就直接加载好了,这里不说太多。主要说说第三方模块怎么加载,这里的第三方包括自己的。
1.准确路径加载:这个准确路径可以是绝对的也可以是相对的,关键是对的就行。
require('/home/marco/foo.js') require('./foo.js')
上述这两种就是直接告诉你模块的具体位置了。
2.模糊查询加载:
/home/ry/projects/foo.js require('bar.js')
例如,在foo中加载'bar.js',它的查找顺序是:
/home/ry/projects/node_modules/bar.js
/home/ry/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
即便是找它也是在各个层级的 node_modules目录里面找。
3.package.json:使用package.json文件申明项目中使用的依赖库。
4.index.js or index.node:
5.通过环境变量从系统中加载模块:
6.Addons:这是C++的方式扩展,暂时用不上。
总结:对于node.js的模块加载机制,没有写的很详细,但是自己应该算是清楚了。其核心是照着commonjs规范来实现的,那么想了解的更清楚,了解一下commonjs规范就行了。这里的package.json实际上是当前模块自己的管理文件,你可以把它当做你的工程描述文件。其中一部分内容就是描述当前工程依赖了那些模块,还有就是当前工程的入口是那一个js文件,包括index.js也是common.js的一部分。