node.js 模块加载

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')

 上述这两种就是直接告诉你模块的具体位置了。

扫描二维码关注公众号,回复: 621455 查看本文章

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

猜你喜欢

转载自lengbingteng.iteye.com/blog/2080627