Node中如何引入一个模块及其细节( 二 )

  • 如果是 NativeModule,则 loadNativeModule 加载模块,如 fs、http、path 等模块,加载结束
  • 否则,使用 Module.load 加载模块,当然这个步骤也很长,下一章节再细讲
  • // <node_internals>/internal/modules/cjs/loader.js:879Module._load = function(request, parent, isMain) {let relResolveCacheIdentifier;if (parent) {// ...}const filename = Module._resolveFilename(request, parent, isMain);const cachedModule = Module._cache[filename];// 如果命中缓存,直接取缓存if (cachedModule !== undefined) {updateChildren(parent, cachedModule, true);return cachedModule.exports;}// 如果是 NativeModule,加载它const mod = loadNativeModule(filename, request);if (mod && mod.canBeRequiredByUsers) return mod.exports;// Don't call updateChildren(), Module constructor already does.const module = new Module(filename, parent);if (isMain) {process.mainModule = module;module.id = '.';}Module._cache[filename] = module;if (parent !== undefined) { // ... }let threw = true;try {if (enableSourceMaps) {try {// 如果不是 NativeModule,加载它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 {// ...}return module.exports;}; require.cache
    「当代码执行 require(lib) 时,会执行 lib 模块中的内容,并作为一份缓存,下次引用时不再执行模块中内容」 。
    这里的缓存指的就是 require.cache,也就是上一段指的 Module._cache
    // <node_internals>/internal/modules/cjs/loader.js:899require.cache = Module._cache; 这里有个小测试:
    ? 有两个文件: index.js 与 utils.js 。utils.js 中有一个打印操作,当 index.js 引用 utils.js 多次时,utils.js 中的打印操作会执行几次 。代码示例如下 ?
    「index.js」
    // index.js// 此处引用两次require('./utils')require('./utils') 「utils.js」
    // utils.jsconsole.log('被执行了一次') 「答案是只执行了一次」,因此 require.cache,在 index.js 末尾打印 require,此时会发现一个模块缓存
    // index.jsrequire('./utils')require('./utils')console.log(require)
    Node中如何引入一个模块及其细节

    文章插图
     
    那回到本章刚开始的问题:
    ? 如何不重启应用热加载模块呢? ?
    答:「删掉 Module._cache」,但同时会引发问题,如这种 一行 delete require.cache 引发的内存泄漏血案[3]
    所以说嘛,这种黑魔法大幅修改核心代码的东西开发环境玩一玩就可以了,千万不要跑到生产环境中去,毕竟黑魔法是不可控的 。
    总结
    1. 模块中执行时会被 module wrapper 包裹,并注入全局变量 require 及 module 等
    2. module.exports 与 exports 的关系实际上是 exports = module.exports
    3. require 实际上是 module.require
    4. require.cache 会保证模块不会被执行多次
    5. 不要使用 delete require.cache 这种黑魔法




    推荐阅读