- 我们知道,在node中每个文件就是一个模块,在模块之间不会造成变量污染和命名冲突等问题。通过exports 或者 module.exports 导出一个模块,通过require 来引入一个模块
- 关于模块的介绍,可参考原先写的一篇博客node学习笔记-模块,这篇博客主要简单介绍了在模块引入时步骤及简单原理,模块在引入时,需要经历以下过程
- 路径解析
- 创建模块
- 尝试加载该模块
- 尝试加载该模块时
- 取出文件的后缀
- 加载模块(读取文件)
- js模块:Module.wrap 包裹读取的内容;使用runInThisContext运行字符串;让字符串执行,this改变成exports
- json模块:直接将读取的内容返回
- 如果在引入模块时,没有添加后缀名需要进行后缀名添加
- 对已经加载过的模块不需要进行重新加载,即对加载过的模块进行缓存
根据上面的过程及描述,代码如下:
let path = require('path');
let fs = require('fs');
let vm = require('vm');
function Module(id) {
this.id = id;
this.exports = {};
}
Module.wrapper = [
'(function (exports, module, require, __dirname, __filename) {',
'})'
];
Module._extensions = {
'.js'(module){
// 读取js中的内容,并包裹读取的内容
let content = fs.readFileSync(module.id, 'utf8');
let fnStr = Module.wrapper[0] + content + Module.wrapper[1];
// 使用runInThisContext运行字符串
let fn = vm.runInThisContext(fnStr);
fn.call(module.exports, module.exports, module, req);
},
'.json'(module){
let content = fs.readFileSync(module.id, 'utf8');
module.exports = content;
}
};
Module._cache = {};
function tryModuleLoad(module) {
let extension = path.extname(module.id);
Module._extensions[extension](module);
}
function req(modulePath){
// 1、路径解析
let absModulePath = path.resolve(__dirname, modulePath);
// 4、对模块添加后缀名
let newPath = absModulePath;
let extNames = Object.keys(Module._extensions);
let index = 0;
while(index < extNames.length){
try{
fs.accessSync(newPath);
break;
}catch(e){
newPath = absModulePath + extNames[index++];
}
}
try{
fs.accessSync(newPath);
}catch(e){
throw new Error('The file you required does not exist!');
}
// 5、对模块进行缓存添加
if (Module._cache[newPath]){
return Module._cache[newPath].exports;
}
// 2、 创建模块
let module = new Module(newPath);
// 5、对模块进行缓存添加
Module._cache[newPath] = module;
// 3、 尝试加载该模块
tryModuleLoad(module);
return module.exports
}
let r = req('./a.js'); // 策略: 默认会增加.js .json
req('./a.js');
console.log(r);