require实现原理

1,346 阅读1分钟

commonjs 规范

  • 每个文件都是一种规范
  • modbule.exports讲它导出
  • 别人要用就require
const p = require('./a');
 console.log(p);
// a.js内容
const person = {
    name: 'jaasdsa',
    age: 12
}
module.exports = person;

实现require

  • Module._load 加载某个模块
  • Module._resolveFileName 解析文件名 解析出绝对路径
  • new Module 创建一个新的模块 id,exports 最终导出结果
  • tryModuleLoad 尝试加载块
// 实现 module类
// (function(exports,require,module,__dirname,__filename){
//     module.exports = 'hello'
//    
// })()
//  return module.exports;

源码

const path = require('path');
const fs = require('fs');
const vm = require('vm');

function Module(id) {
    this.id = id;
    this.exports = {};
}
Module._resolveFileName = function(id) {
    let absPath = path.resolve(id);
    if (fs.existsSync(absPath))
        return absPath;
    let extenisons = Object.keys(Module.extenisons);
    for (let i = 0; i < extenisons.length; i++) {
        let ext = extenisons[i];
        let currentPath = absPath + ext;
        if (fs.existsSync(currentPath))
            return currentPath;
    }
    throw new Error('文件不存在');
}

Module.wrapper = ['(function (exports, require, module, __dirname, __filename) {\r\n',
    '\r\n})'
]

Module.extenisons = {};
Module.extenisons['.js'] = function(module) {
    let script = fs.readFileSync(module.id, 'utf-8');
    let content = Module.wrapper[0] + script + Module.wrapper[1];
    let fn = vm.runInThisContext(content);
    let __dirname = path.dirname(module.id);
    fn.call(module.exports, module.exports, req, module, __dirname, module.id);
}
Module.extenisons['.json'] = function(module) {
    let script = fs.readFileSync(module.id, 'utf-8');
    module.exports = JSON.parse(script);
}

Module._cache = new Map();

function tryModuleLoad(module) {
    let ext = path.extname(module.id);
    Module.extenisons[ext](module);
}

function req(id) {
    let fileName = Module._resolveFileName(id);
    if (Module._cache.has(fileName))
        return Module._cache.get(fileName).exports;

    let module = new Module(fileName);
    Module._cache.set(module.id, module);
    tryModuleLoad(module);
    return module.exports;
}

测试

let aa = req('./a');
console.log(aa);