模块分类
- 内置模块:Node源码编译时写入到二进制文件中
- 文件模块:代码运行时,动态加载
- 路径分析:依据标识符确定模块位置(路径标识符、非路径标识符)
- 文件定位:确定目标模块中具体的文件及文件类型(存在'm1'模块,导入时使用require('m1')语法,使用m1.js->m1.json->m1.node的顺序,如果都没找到,会被当做一个目录,查找package.json文件,使用JOSN.parse()解析。接下来会找main.js->main.json->main.node。将index作为目标模块中的具体文件名称)
- 编译执行:采用对应的方式完成文件的编译执行(将某个具体类型的文件按照相应的方式进行编译和执行,创建新对象,按路径载入,完成编译执行,返回可用exports对象)
- 提高模块加载速度
- 当前模块不存在,则经历一次完整加载流程
- 模块加载完成后,使用路径作为索引进行缓存
核心逻辑
- 路径分析
- 缓存优化
- 文件定位
- 编译执行
const fs = require("fs");
const path = require("path");
const vm = require("vm");
function Module(id) {
this.id = id;
this.exports = {};
}// 静态方法
Module._resolveFilename = function (filename) {
// 利用path将filename转为绝对路径
let absPath = path.resolve(__dirname, filename);
console.log(absPath);
// 判断当前路径对应的内容是否存在
if (fs.existsSync(absPath)) {
// 条件成立说明absPath对应的内容是存在的
return absPath;
} else {
//根据文件定位的顺序依次去找
let suffix = Object.keys(Module.__extensions);
for (var i = 0;
i < suffix.length;
i++) {
// 拼接
let newPath = absPath + suffix[i];
if (fs.existsSync(newPath)) {
return newPath;
}
}
}
throw new Error(`${filename} is not exists`);
};
Module.__extensions = {
".js"(module) {
// 读取
let content = fs.readFileSync(module.id, "utf-8");
// 包装
content = Module.wrapper[0] + content + Module.wrapper[1];
// 使用vm执行
let compileFn = vm.runInThisContext(content);
// 转换成一个可执行匿名函数
// 准备参数的值
let exports = module.exports;
let dirname = path.dirname(module.id);
let filename = module.id;
// 调用
compileFn.call(exports, exports, myRequire, module, filename, dirname);
},
".json"(module) {
//读取然后格式化
let content = JSON.parse(fs.readFileSync(module.id, "utf-8"));
module.exports = content;
},
};
Module.wrapper = [
"(function(exports,require,module,__filename,__dirname){",
"})",
];
Module._cache = {};
Module.prototype.load = function () {
let extname = path.extname(this.id);
console.log("extname", extname);
Module.__extensions[extname](this);
};
function myRequire(filename) {
// 1,获取绝对路径
let modulePath = Module._resolveFilename(filename);
//2,实现缓存优先
let cacheModule = Module._cache[modulePath];
if (cacheModule) {
return cacheModule.exports;
}
// 3创建空对象加载目标模块
let module = new Module(modulePath);
// 4,缓存已经加载的模块
Module._cache[modulePath] = module;
// 5,执行加载(编译执行)
module.load();
//6 ,返回数据
return module.exports;
}let obj = myRequire("./m");
console.log(obj);