实现简易版commonjs规范-------require方法

2019-10-22  本文已影响0人  成熟稳重的李先生

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);
  }
}
上一篇下一篇

猜你喜欢

热点阅读