具体实现步骤:
- 导入相关模块,创建一个Require方法。
- 抽离通过Module._load方法,用于加载模块。
- Module.resolveFilename 根据相对路径,转换成绝对路径。
- 缓存模块 Module._cache,同一个模块不要重复加载,提升性能。
- 创建模块 id: 保存的内容是 exports = {}相当于this。
- 利用tryModuleLoad(module, filename) 尝试加载模块。
- Module._extensions使用读取文件。
- Module.wrap: 把读取到的js包裹一个函数。
- 将拿到的字符串使用runInThisContext运行字符串。
让字符串执行并将this改编成exports。
function Require(modulePath) {
// 获取当前要加载的绝对路径
let absPathname = path.resolve(__dirname, modulePath);
// 获取所有后缀名
const extNames = Object.keys(Module._extensions);
let index = 0;
// 存储原始文件路径
const oldPath = absPathname;
function findExt(absPathname) {
if (index === extNames.length) {
return throw new Error('文件不存在');
}
try {
fs.accessSync(absPathname);
return absPathname;
} catch(e) {
const ext = extNames[index++];
findExt(oldPath + ext);
}
}
// 递归追加后缀名,判断文件是否存在
absPathname = findExt(absPathname);
// 从缓存中读取,如果存在,直接返回结果
if (Module._cache[absPathname]) {
return Module._cache[absPathname].exports;
}
// 创建模块,新建Module实例
const module = new Module(absPathname);
// 添加缓存
Module._cache[absPathname] = module;
// 加载当前模块
tryModuleLoad(module);
// 返回exports对象
return module.exports;
}
上述代码具体步骤:
获取模块的绝对路径和后缀名
- 递归追加后缀名,判断文件是否存在