commonjs是服务端的js,每个文件就是一个模块,它有自己独立的作用域,并且不存在异步的情况,commonjs提供了一个方法 require,它可以同步的将依赖引入,下边我们来仿写一个require函数,达到同步引用文件的效果
let path = require("path");
let vm = require("vm"); // 提供一个沙箱环境,不受外部变量的影响
let fs = require("fs");
function req(filename){
let absPath = path.resolve(__dirname, filename); // 获取文件绝对路径
let index = 0;
// let
let extnames = Object.keys(Module._extensions); //获取后缀“池”
let old = absPath; // 保存路径名(可能不带后缀)
if(Module._cache[absPath]){ // 如果请求过这个文件,那么缓存起来,下次直接拿缓存
return Module._cache[absPath].exports;
}
function find(filename) { // 格式化生成一个准确的路径(即加上“.js”或者“.json”后缀)
if(index === extnames.length){
return filename;
}
try {
fs.accessSync(filename); // 是否有这个文件(如果没有,则会跳入catch中)
return filename; // 有就返回这个格式化后的路径
}catch(ex){
return find(old+extnames[index++]);
}
}
absPath = find(absPath); // 格式化路径
try {
fs.readFileSync(absPath); // 判断是否有这个文件
}catch(ex){
throw new Error("文件不存在");
}
let module = new Module(absPath);
Module._cache[module.id] = module; // 将这个模块缓存起来
tryModuleLoad(module); // 加载它(即执行module.exports赋值操作)
return module.exports;
}
function Module(pathname){
this.id = pathname;
this.exports = {};
}
function tryModuleLoad(module) {
let extname = path.extname(module.id);
Module._extensions[extname](module);
}
Module._cache = {};
Module.wrap = ["(function(exports, module, require, __filename, __dirname){",
"})"] // 工具数组,与要引入的文件合并成一个函数
Module._extensions = {
".js"(module) {
let content = fs.readFileSync(module.id);
let fnStr = Module.wrap[0] + content + Module.wrap[1];
let fn = vm.runInThisContext(fnStr);
fn.call(module.exports, module.exports, module, req);
},
".json"(module) {
let content = fs.readFileSync(module.id);
module.exports = JSON.parse(content);
}
}
网友评论