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}`




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM