nodeJS中require方法的自实现

nodeJS模块化,使用commonJS规范,该规范以读取文件实现模块化。

(function(exports,require, module, __filename,__dirname) {
  module.export = XXX;
  return module.exports;
})

1. commonJS规范:

1. 文件即模块。(读取的文件是字符串)
2. 定义了导出文件的方式module.exports 和 exports
3. 定义了引入文件的方式require
 
**浏览器中让字符串运行**
eval / new Function
**nodeJS中字符串运行**
const vm = require('vm'); vm.runInThisContext(str);

2. require方法的实现

* 内部实现了一个内置的require方法
* 使用`Module._load`方法加载模块
* 使用`Module.__resolveFilename`,将相对路径=>绝对路径+文件后缀
* 缓存机制`Module._cache`,缓存模块
* 新建一个模块 `new Module`;Module有两个主要属性id(路径), exports={}
* 使用`tryModuleLoad`尝试加载模块
    * 获取文件后缀
    * 通过`Module._extensions`上后缀对应的方法加载模块->读取文件
    * `Module.wrap`包裹读取的字符串内容;
    * `runInThisContext`运行包裹后的字符串;将字符串转为函数
    * 运行函数,并将this.exports(默认空对象)作为this绑定到函数的this上
let path = require('path');
let fs = require('fs');
let vm = require('vm');

function Module(path){
  this.id = path;
  this.exports = {};
}
Module.wrapper = [
  '(function(exports, require, module, __filename,__dirname){',
  '})'
]
Module.extensions = {
  '.js': function(module) {
    let content = fs.readFileSync(module.id, 'utf8');
    let fnStr = Module.wrapper[0] + content + Module.wrapper[1];
    let wrapperFn = vm.runInThisContext(fnStr);
    wrapperFn.call(module.exports, module.exports, req, module, __filename, __dirname);
    // 该方法是用户自定义给module.exports赋值;
  }, 
  '.json': function(module) {
    let json = fs.readFileSync(module.id, 'utf8');
    module.exports = json;
  },
  '.node': {
    //
  }
}
function tryModuleLoad(module) {
  let extension = path.extname(module.id);

  Module.extensions[extension](module);
}
Module._cache = {};
function req(modulePath) {
  let resolvedPath = path.resolve(modulePath);
  // 如果路径的文件未写扩展名;需要按照默认扩展名依次查找
  let i = 0;
  function findFilePath(parsePath) {
    try {
      fs.accessSync(parsePath);
      return parsePath;
    } catch(e){
      let extensions = Object.keys(Module.extensions);
      let tempPath= resolvedPath + extensions[i++];
      return findFilePath(tempPath);
    }
  }
  let absolutePath = findFilePath(resolvedPath);
  if(Module._cache[absolutePath]) {
    return Module._cache[absolutePath].exports;
  }
  const module = new Module(absolutePath);
  tryModuleLoad(module);
  Module._cache[absolutePath] = module;
  return module.exports;
}

console.log(req('./1'));

3. module.exports和exports的区别

1. exports是module.exports的别名,两者指向地址相同;
2. 但是模块导出的是module.exports。如果使用`exports = xxxx`导出,则exports地址修改。
而module.exports初始值是{},则模块最终导出的是{}
3. 如果一定要用exports,则可以通过给exports对象添加属性,则相当于同时给module.exports添加。
如:`exports.a = xxx;`
最终的导出结果是`{a: xxx}`



原文地址:https://www.cnblogs.com/lyraLee/p/12169469.html