require() 方法详解
在 NodeJS 中有一个方法是我们使用频率最高的,那就是 require 方法。NodeJs 遵循 CommonJS 规范,该规范的核心是通过 require来加载其他依赖的模块。
几个问题
什么是 CommonJS
每一个文件就是一个模块,拥有自己独立的作用域,变量,以及方法等,对其他的模块都不可见。CommonJS 规范规定,每个模块内部,module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即)是对外的接口。
Node 模块的分类
module 对象
NodeJs 内部提供一个 Module 构建函数。所有模块都是 Module 的实例。
每个模块内部,都有一个 module 对象,代表当前模块。它有以下属性。
module 对象的属性
模块的识别符,通常是带有绝对路径的模块文件名。
模块的文件名,带有绝对路径。
返回一个布尔值,表示模块是否已经完成加载。
返回一个对象,表示调用该模块的模块(程序入口文件的module.parent为null)
返回一个数组,表示该模块要用到的其他模块。
表示模块对外输出的值。
module.exports 属性属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取变量。属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取变量。
exports 变量
我们有时候会这么写:
// test.js
function test(){
console.log(test);
}
export.test = test;
// result.js
const test = require("./test")
这样也可以拿到正确的结果,这是因为:exports 变量指向 module.exports。这等同在每个模块头部,有一行这样的命令。
var exports = module.exports;
注意:不能直接给 exports 变量赋值,这样会改变 exports 的指向,不再指向 module.exports。在其他模块使用 require 方法是拿不到赋给 exports 的值的,因为 require 方法获取的是其他模块的 module.exports 的值。
建议:尽可能的使用 来导出结果。
模块的流程
创建模块
导出模块
加载模块
使用模块
require 方法
require 是 node 用来加载并执行其它文件导出的模块的方法。
在 NodeJs 中,我们引入的任何一个模块都对应一个 Module 实例,包括入口文件。
完整步骤:
require = function require(path) {
return mod.require(path);
};
const filename = Module._resolveFilename(request, parent, isMain);
const module = cachedModule || new Module(filename, parent);
if (isMain) {
process.mainModule = module;
module.id = '.';
}
Module._cache[filename] = module;

module.load(filename);
const extension = findLongestRegisteredExtension(filename);
如果后缀名称是ES Module格式的(.mjs),则判断Module是否支持.mjs文件的解析,如果不支持,则抛出异常。
Module._extensions[extension](this, filename);
content = fs.readFileSync(filename, 'utf8');
module._compile(content, filename);
主要内容步骤:
const compiledWrapper = wrapSafe(filename, content, this);
const dirname = path.dirname(filename);
const require = makeRequireFunction(this, redirects);
let result;
const exports = this.exports;
const thisValue = exports;
const module = this;
result = compiledWrapper.call(thisValue, exports, require, module, filename, dirname);
return result;
方法的返回值

具体获得上图结果的代码是:
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));
},
});
this.loaded = true;
总结
通过上面的调试过程可得出以下结论:


~本文完~
学习有趣的知识,结识有趣的朋友,塑造有趣的灵魂!
大家好!我是〖编程三昧〗的作者 隐逸王,我的公众号是『编程三昧』,欢迎关注,希望大家多多指教!
知识与技能并重,内力和外功兼修,理论和实践两手都要抓、两手都要硬!