require工做原理

在日常開發中,當咱們須要用一個模塊的時候,只須要require一下就好了,可是對於其內部的原理,不必定都清楚。讀《深刻淺出的nodejs》的時候,咱們會發現,書中提到,每個模塊在編譯過程當中,node都會在模塊外面封裝一層,(function(exports, require, module, __filename, __dirname){})。可是真正調用require的時候,發生了什麼,必須得細究一下。node

require大體過程

  • require方法
Module.prototype.require = function(id) {
  if (typeof id !== 'string') {
    throw new ERR_INVALID_ARG_TYPE('id', 'string', id);
  }
  if (id === '') {
    throw new ERR_INVALID_ARG_VALUE('id', id,'must be a non-empty string');
  }
  return Module._load(id, this, /* isMain */ false);
};
複製代碼
  • _load方法
Module._load = function(request, parent, isMain) {
  if (parent) {
    debug('Module._load REQUEST %s parent: %s', request, parent.id);
  }

  var filename = Module._resolveFilename(request, parent, isMain);

  var cachedModule = Module._cache[filename];
  if (cachedModule) {
    updateChildren(parent, cachedModule, true);
    return cachedModule.exports;
  }

  if (NativeModule.nonInternalExists(filename)) {
    debug('load native module %s', request);
    return NativeModule.require(filename);
  }

  // Don't call updateChildren(), Module constructor already does.
  var module = new Module(filename, parent);

  if (isMain) {
    process.mainModule = module;
    module.id = '.';
  }

  Module._cache[filename] = module;

  tryModuleLoad(module, filename);

  return module.exports;
};
複製代碼

從上述兩個函數中,require的大體過程以下:緩存

一、先檢測傳入的id是否有效。 二、若是有效,則調用Module._load方法,該方法主要負責加載新模塊和管理模塊的緩存,而require自己就是對該方法的一個封裝。 三、而後會調用Module._resolveFilename去取文件地址。 四、判斷是否有緩存模塊,若是有返回緩存模塊的exports。 五、若是沒有緩存,在檢測文件名是不是核心模塊,若是是調用核心模塊的require。 六、若是不是核心模塊,那麼,建立新的一個module對象。 七、在 Module._cache中緩存該對象, 八、返回模塊自己的exports對象。函數

上述的解讀中,咱們拋開了兩個沒有談,一個是Module._resolveFilename()方法,還有一個是tryModuleLoad()方法。測試

filename的獲取

Module._resolveFilename = function(request, parent, isMain, options) {
  if (NativeModule.nonInternalExists(request)) {
    return request;
  }

  var paths;

  if (typeof options === 'object' && options !== null &&
      Array.isArray(options.paths)) {
    const fakeParent = new Module('', null);

    paths = [];

    for (var i = 0; i < options.paths.length; i++) {
      const path = options.paths[i];
      fakeParent.paths = Module._nodeModulePaths(path);
      const lookupPaths = Module._resolveLookupPaths(request, fakeParent, true);

      if (!paths.includes(path))
        paths.push(path);

      for (var j = 0; j < lookupPaths.length; j++) {
        if (!paths.includes(lookupPaths[j]))
          paths.push(lookupPaths[j]);
      }
    }
  } else {
    paths = Module._resolveLookupPaths(request, parent, true);
  }
  var filename = Module._findPath(request, paths, isMain);
  if (!filename) {
    var err = new Error(`Cannot find module '${request}'`);
    err.code = 'MODULE_NOT_FOUND';
    throw err;
  }
  return filename;
};
複製代碼

_resolveFilename的大體流程:ui

一、查詢文件名是不是核心模塊,若是是直接返回傳入的id 二、由於option沒有參數傳入,因此會調用 Module._resolveLookupPaths方法去獲取路徑 三、調用Module._findPath方法this

咱們能夠寫以下測試代碼:spa

console.log(require('module')._resolveFilename('./lodash'));
let paths = require('module')._resolveLookupPaths('./lodash');
console.log(paths);
console.log(require('module')._findPath("./lodash", paths[1]));
複製代碼

輸出的結果:prototype

/Users/laihuamin/Documents/learn-record/node_modules/lodash/lodash.js
[ './lodash',
  [ '.',
    '/Users/laihuamin/Documents/learn-record/node_modules',
    '/Users/laihuamin/Documents/node_modules',
    '/Users/laihuamin/node_modules',
    '/Users/node_modules',
    '/node_modules',
    '/Users/laihuamin/.node_modules',
    '/Users/laihuamin/.node_libraries',
    '/Users/laihuamin/.nvm/versions/node/v6.9.1/lib/node' ] ]
/Users/laihuamin/Documents/learn-record/node_modules/lodash/lodash.js
複製代碼

_resolveLookupPaths:其實就是node解析模塊中的路徑查找,他會向父目錄查找,直到根目錄爲止。 _findPath:其實就是將_resolveLookupPaths查找出來的文件名和文件id向匹配,返回一個文件地址。debug

tryModuleLoad

function tryModuleLoad(module, filename) {
  var threw = true;
  try {
    module.load(filename);
    threw = false;
  } finally {
    if (threw) {
      delete Module._cache[filename];
    }
  }
}
複製代碼

在拿到文件地址以後,module會調用這個方法取tryModuleLoad,去嘗試加載模塊,若是報錯,那麼清除模塊的緩存。code

相關文章
相關標籤/搜索