1_q9myzo5Au8OfsaSrCodNmw4.png

用户模块

用户模块是node开发者编写的javaScript文件或者json文件。它以文件的形式去组织。通过require()函数去获取其他模块的导出对象。和通过exports对象或者module.exports对象导出函数或者变量给其它模块使用。在编写node代码的时候。开发者并不需要去处理模块加载的事情。这些node的模块化系统已经为我们处理好。
image.png

  1. const http = require("http");
  2. const utisl = require('./utils');
  3. const app = http.createServer();
  4. app.on("request", (req, res) => {
  5. res.end(utils.getResponse(req,res));
  6. });
  7. app.listen(4000);

当我们以上面的例子作为启动模块。模块名为man.js。下面是工程目录。在工程目录路径下执行 node main.js

|- project |- nodele_module |- package.json |- main.js |- utils.js

在上面node的启动流程我们知道。node通过internal\modules\run_main.jsexecuteUserEntryPoint启动用户主模块。在这里我们只讨论commonjs的启动方式。

function executeUserEntryPoint(main = process.argv[1]) {
  const resolvedMain = resolveMainPath(main);
  const useESMLoader = shouldUseESMLoader(resolvedMain);
  if (useESMLoader) {
    runMainESM(resolvedMain || main);
  } else {
    // 启动主模块   
    Module._load(main, null, true);
  }
}

通过 Module._load(main, null, true);启动了用户主模块。Module对象的定义在 internal\modules\cjs\loader.js下。

function Module(id = '', parent) {
  this.id = id;
  this.path = path.dirname(id);
  this.exports = {};
  // 设置更新夫模块引用
  moduleParentCache.set(this, parent);
  // 更新主模块引用
  updateChildren(parent, this, false);
  this.filename = null;
  this.loaded = false;
  this.children = [];
}

Module为一个构造函数。_load() 在后面进行动态挂载。_load()函数有三个参数。(先吐槽一下,node的这样设计,真的太他妈恶心了)

/*
* @params  request{string}模块的绝对路地址。
* @params  parent{Object}父模块,为当前Module构造函数的实例。在加载主模块的时候为null,因为主模块没有夫模块。
* @params  isMain{Boolean} 是否为主模块。在加载主模块的时候为true
*/
Module._load = function(request, parent, isMain) {
  let relResolveCacheIdentifier;
  // 模块是否存在夫模块
  if (parent) {
    relResolveCacheIdentifier = `${parent.path}\x00${request}`;
    const filename = relativeResolveCache[relResolveCacheIdentifier];
    if (filename !== undefined) {
      const cachedModule = Module._cache[filename];
      if (cachedModule !== undefined) {
        updateChildren(parent, cachedModule, true);
        if (!cachedModule.loaded)
          return getExportsForCircularRequire(cachedModule);
        return cachedModule.exports;
      }
      delete relativeResolveCache[relResolveCacheIdentifier];
    }
  }
  // 获取模块的名称。当在以目录作为启动模块,node会默认以该目录下的index.js作为模块加载
  const filename = Module._resolveFilename(request, parent, isMain);
  // 判断文件名是否以node:开头。在内建javaScript的javaScript模块中,最终的内建模块被重新命名为例如fs的的别名node:fs
  // 所以我们能通过Module._load("node:fs")的方式去价值内建javaScript模块
  if (StringPrototypeStartsWith(filename, 'node:')) {
    // 获取内建模块的id例如 node:fs。获取fs
    const id = StringPrototypeSlice(filename, 5);
        // 加载内建javaScript模块
    const module = loadNativeModule(id, request);
    // 如果模块没有找到,或者改模块是一个不能被用户模块引用的模块。就抛出异常。
    if (!module?.canBeRequiredByUsers) {
      throw new ERR_UNKNOWN_BUILTIN_MODULE(filename);
    }

    return module.exports;
  }
  // 通过文件名作为key, 模块的导出对象 exports作为value缓存在Module构造函数的_cache对象上。
  const cachedModule = Module._cache[filename];
  // 如果有缓存
  if (cachedModule !== undefined) {
    // 更新子模块的引用
    updateChildren(parent, cachedModule, true);
    if (!cachedModule.loaded) {
      const parseCachedModule = cjsParseCache.get(cachedModule);
      if (!parseCachedModule || parseCachedModule.loaded)
        return getExportsForCircularRequire(cachedModule);
      parseCachedModule.loaded = true;
    } else {
      return cachedModule.exports;
    }
  }
  // 获取 内建javaScript模块
  const mod = loadNativeModule(filename, request);
  // 判断模块是否能做在用户模块中引用
  if (mod?.canBeRequiredByUsers) return mod.exports;

  // 构建一个模块对象
  const module = cachedModule || new Module(filename, parent);

  if (isMain) {
    process.mainModule = module;
    module.id = '.';
  }
  // 缓存起来
  Module._cache[filename] = module;
  if (parent !== undefined) {
    relativeResolveCache[relResolveCacheIdentifier] = filename;
  }

  let threw = true;
  try {
    if (getSourceMapsEnabled()) {
      try {
        // 加载主模块
        module.load(filename);
      } catch (err) {
        rekeySourceMap(Module._cache[filename], err);
        throw err; /* node-do-not-add-exception-line */
      }
    } else {
      module.load(filename);
    }
    threw = false;
  } finally {
    if (threw) {
      delete Module._cache[filename];
      if (parent !== undefined) {
        delete relativeResolveCache[relResolveCacheIdentifier];
        const children = parent?.children;
        if (ArrayIsArray(children)) {
          const index = ArrayPrototypeIndexOf(children, module);
          if (index !== -1) {
            ArrayPrototypeSplice(children, index, 1);
          }
        }
      }
    } else if (module.exports &&
               !isProxy(module.exports) &&
               ObjectGetPrototypeOf(module.exports) ===
                 CircularRequirePrototypeWarningProxy) {
      ObjectSetPrototypeOf(module.exports, ObjectPrototype);
    }
  }

  return module.exports;
};
// 执行模块加载的真正逻辑
Module.prototype.load = function(filename) {

  this.filename = filename;
  this.paths = Module._nodeModulePaths(path.dirname(filename));
  // 获取模块的扩展处理函数
  const extension = findLongestRegisteredExtension(filename);
  // 如果模块以mjs为后缀并且没有mjs的处理函数
  if (StringPrototypeEndsWith(filename, '.mjs') && !Module._extensions['.mjs'])
    throw new ERR_REQUIRE_ESM(filename);
  // 调用模块的处理函数。在这里为Module._extensions[js]函数
  Module._extensions[extension](this, filename);
  this.loaded = true;

  const ESMLoader = asyncESM.ESMLoader;
  const exports = this.exports;
  if ((module?.module === undefined ||
       module.module.getStatus() < kEvaluated) &&
      !ESMLoader.cjsCache.has(this))
    ESMLoader.cjsCache.set(this, exports);
};

Module._extensions['.js'] = function(module, filename) {
  // 如果文件以js为后缀,并却package.json文件中存在tye = "module"将抛出异常
  if (StringPrototypeEndsWith(filename, '.js')) {
    const pkg = readPackageScope(filename);
    if (pkg?.data?.type === 'module') {
      const parent = moduleParentCache.get(module);
      const parentPath = parent?.filename;
      const packageJsonPath = path.resolve(pkg.path, 'package.json');
      throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
    }
  }
  // 获取文件的内容缓存
  const cached = cjsParseCache.get(module);
  let content;
  // 如果没有缓存
  if (cached?.source) {
    content = cached.source;
    cached.source = undefined;
  } else {
    // 使用fs模块同步读取模块
    content = fs.readFileSync(filename, 'utf8');
  }
  // 最终交给module._compile函数编译执行
  module._compile(content, filename);
};
// 处理json文件
Module._extensions['.json'] = function(module, filename) {
  const content = fs.readFileSync(filename, 'utf8');

  if (policy?.manifest) {
    const moduleURL = pathToFileURL(filename);
    policy.manifest.assertIntegrity(moduleURL, content);
  }

  try {
    module.exports = JSONParse(stripBOM(content));
  } catch (err) {
    err.message = filename + ': ' + err.message;
    throw err;
  }
};


//处理.node文件
Module._extensions['.node'] = function(module, filename) {
  if (policy?.manifest) {
    const content = fs.readFileSync(filename);
    const moduleURL = pathToFileURL(filename);
    policy.manifest.assertIntegrity(moduleURL, content);
  }
  // Be aware this doesn't use `content`
  return process.dlopen(module, path.toNamespacedPath(filename));
};

Module.prototype._compile = function(content, filename) {
  let moduleURL;
  let redirects;
  // node的安全策略,这里不做解析
  if (policy?.manifest) {
    moduleURL = pathToFileURL(filename);
    redirects = policy.manifest.getDependencyMapper(moduleURL);
    policy.manifest.assertIntegrity(moduleURL, content);
  }
  maybeCacheSourceMap(filename, content, this);
  // 包装成一个函数,解析在下面
  const compiledWrapper = wrapSafe(filename, content, this);

  let inspectorWrapper = null;
  // 断点调式相关,这里不做解析
  if (getOptionValue('--inspect-brk') && process._eval == null) {
    if (!resolvedArgv) {
      if (process.argv[1]) {
        try {
          resolvedArgv = Module._resolveFilename(process.argv[1], null, false);
        } catch {
          assert(ArrayIsArray(getOptionValue('--require')));
        }
      } else {
        resolvedArgv = 'repl';
      }
    }

    if (resolvedArgv && !hasPausedEntry && filename === resolvedArgv) {
      hasPausedEntry = true;
      inspectorWrapper = internalBinding('inspector').callAndPauseOnStart;
    }
  }
  // 获取文件的目录
  const dirname = path.dirname(filename);
  // 构建一个require函数
  const require = makeRequireFunction(this, redirects);
  let result;
  const exports = this.exports;
  const thisValue = exports;
  const module = this;
  if (requireDepth === 0) statCache = new SafeMap();
  if (inspectorWrapper) {
    result = inspectorWrapper(compiledWrapper, thisValue, exports,
                              require, module, filename, dirname);
  } else {
    // 执行模块
    result = ReflectApply(compiledWrapper, thisValue,
                          [exports, require, module, filename, dirname]);
  }
  hasLoadedAnyUserCJSModule = true;
  if (requireDepth === 0) statCache = null;
  return result;
}

// 对文件的内容进行一层包装
function wrapSafe(filename, content, cjsModuleInstance) {
  if (patched) {
    // 进行模块的包装
    const wrapper = Module.wrap(content);
    return vm.runInThisContext(wrapper, {
      filename,
      lineOffset: 0,
      displayErrors: true,
      importModuleDynamically: async (specifier) => {
        const loader = asyncESM.ESMLoader;
        return loader.import(specifier, normalizeReferrerURL(filename));
      },
    });
  }
  try {
    return vm.compileFunction(content, [
      'exports',
      'require',
      'module',
      '__filename',
      '__dirname',
    ], {
      filename,
      importModuleDynamically(specifier) {
        const loader = asyncESM.ESMLoader;
        return loader.import(specifier, normalizeReferrerURL(filename));
      },
    });
  } catch (err) {
    if (process.mainModule === cjsModuleInstance)
      enrichCJSError(err);
    throw err;
  }
}
let patched = false;

// eslint-disable-next-line func-style
let wrap = function(script) {
  return Module.wrapper[0] + script + Module.wrapper[1];
};

const wrapper = [
  '(function (exports, require, module, __filename, __dirname) { ',
  '\n});',
];

let wrapperProxy = new Proxy(wrapper, {
  set(target, property, value, receiver) {
    patched = true;
    return ReflectSet(target, property, value, receiver);
  },

  defineProperty(target, property, descriptor) {
    patched = true;
    return ObjectDefineProperty(target, property, descriptor);
  }
});

ObjectDefineProperty(Module, 'wrap', {
  get() {
    return wrap;
  },

  set(value) {
    patched = true;
    wrap = value;
  }
});

ObjectDefineProperty(Module, 'wrapper', {
  get() {
    return wrapperProxy;
  },

  set(value) {
    patched = true;
    wrapperProxy = value;
  }
});

Module.prototype._compile 通过makeRequireFunction(this, redirects);创建了一个require函数。makeRequireFunction函数位于internal\modules\cjs\helpers.js文件内。

function makeRequireFunction(mod, redirects) {
  const Module = mod.constructor;

  let require;
  // 是否直接获取。例如可以require("node:fs")
  if (redirects) {
    const id = mod.filename || mod.id;
    const conditions = cjsConditions;
    const { resolve, reaction } = redirects;
    require = function require(specifier) {
      let missing = true;
      const destination = resolve(specifier, conditions);
      if (destination === true) {
        missing = false;
      } else if (destination) {
        const href = destination.href;
        if (destination.protocol === 'node:') {
          const specifier = destination.pathname;
          const mod = loadNativeModule(specifier, href);
          if (mod && mod.canBeRequiredByUsers) {
            return mod.exports;
          }
          throw new ERR_UNKNOWN_BUILTIN_MODULE(specifier);
        } else if (destination.protocol === 'file:') {
          let filepath;
          if (urlToFileCache.has(href)) {
            filepath = urlToFileCache.get(href);
          } else {
            filepath = fileURLToPath(destination);
            urlToFileCache.set(href, filepath);
          }
          return mod.require(filepath);
        }
      }
      if (missing) {
        reaction(new ERR_MANIFEST_DEPENDENCY_MISSING(
          id,
          specifier,
          ArrayPrototypeJoin([...conditions], ', ')
        ));
      }
      return mod.require(specifier);
    };
  } else {
    require = function require(path) {
      return mod.require(path);
    };
  }

  function resolve(request, options) {
    validateString(request, 'request');
    return Module._resolveFilename(request, mod, false, options);
  }

  require.resolve = resolve;

  function paths(request) {
    validateString(request, 'request');
    return Module._resolveLookupPaths(request, mod);
  }

  resolve.paths = paths;

  require.main = process.mainModule;
  require.extensions = Module._extensions;

  require.cache = Module._cache;

  return require;
}

所以一个完整的用户模块的加载流程为。