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的一部分。