浅析node.js的模块加载机制

2020-06-17 06:24:19易采站长站整理

};

//根据moduleId解析绝对路径,
Module._resolveFileName = function(moduleId) {
let p = path.resolve(moduleId);

if (!path.extname(moduleId)) { //传入的模块没有后缀
let arr = Object.keys(Module._extensions);

//循环读取不同扩展名的文件
for (var i = 0; i < arr.length; i++) {
let file = p + arr[i]; //拼接上后缀名成为一个完整的路径
try {
fs.accessSync(file);
return file; //若此文件存在返回它
} catch (e) {
console.log(e);
}
}
} else {
return p;
}
};

function req(moduleId) {
let file = Module._resolveFileName(moduleId);

if (Module._cacheModule[file]) { //若缓存中存在此模块
return Module._cacheModule[file];
} else {
let module = new Module(file);
module.exports = module.load(file);
return module.exports;
}
}

console.log(req('./a.js')());

a.js的文件内容:


module.exports = function() {
console.log('This message from a.js');
console.log(__dirname);
console.log(__filename);
}

最终运行结果:

This message from a.js
/Users/cuiyue/workspace/test
/Users/cuiyue/workspace/test/a.js

重要代码说明

_resolveFileName

_resolveFileName方法的主要作用是把传入的模块解析成绝对路径,这样才可以进行下一步,根据完整的路径加载模块。

因此要进行判断,如果传入的模块不存在,则要报错;如果传入的模块已经有扩展名了,就不要拼接了;若没有扩展名,依次以.js .json .node的顺序拼接成完成的模块进行加载。

_extensions

此对象中封装了加载不同类型模块的处理方法,其中若是.json类型则使用fs读取文件直接转换成JSON对象并返回。

若是.js文件则读取后,拼接闭包,将exports,require,module,__dirname,__filename五大参数拼接好,使用vm模块的沙箱机制运行,得到的结果放入module.exports返回。

总结

以上就是node.js的模块加载的简单逻辑,实际上node.js的源码远远比上面的代码复杂,光是处理模块路径、判断合法等操作就写了N行。而且我这里没有写缓存以及其它的复杂逻辑,但核心差不多就是这些,核心的核心就是用fs.readFileSync读取js文件,把内容拼接到一个大大的闭包中,这也解释了为什么我们自己写的所有node模块中都会有require方法,exports导出,以及__dirname和__filename参数。

了解了node.js的模块加载逻辑,在以后写node.js就更可避免一些误解,写出精细的代码。