commonJs-模块引入require的简单实现

  • 我们知道,在node中每个文件就是一个模块,在模块之间不会造成变量污染和命名冲突等问题。通过exports 或者 module.exports 导出一个模块,通过require 来引入一个模块
  • 关于模块的介绍,可参考原先写的一篇博客node学习笔记-模块,这篇博客主要简单介绍了在模块引入时步骤及简单原理,模块在引入时,需要经历以下过程
    • 路径解析
    • 创建模块
    • 尝试加载该模块
  • 尝试加载该模块时
    • 取出文件的后缀
    • 加载模块(读取文件)
      • js模块:Module.wrap 包裹读取的内容;使用runInThisContext运行字符串;让字符串执行,this改变成exports
      • json模块:直接将读取的内容返回
    • 如果在引入模块时,没有添加后缀名需要进行后缀名添加
    • 对已经加载过的模块不需要进行重新加载,即对加载过的模块进行缓存

根据上面的过程及描述,代码如下:

    let path = require('path');
    let fs = require('fs');
    let vm = require('vm');
    
    function Module(id) {
        this.id = id;
        this.exports = {};
    }
    
    Module.wrapper = [
        '(function (exports, module, require, __dirname, __filename) {',
        '})'
    ];
    
    Module._extensions = {
        '.js'(module){
            // 读取js中的内容,并包裹读取的内容
            let content = fs.readFileSync(module.id, 'utf8');
            let fnStr = Module.wrapper[0] + content + Module.wrapper[1];
            // 使用runInThisContext运行字符串
            let fn = vm.runInThisContext(fnStr);
            fn.call(module.exports, module.exports, module, req);
        },
        '.json'(module){
            let content = fs.readFileSync(module.id, 'utf8');
            module.exports = content;
        }
    };
    
    Module._cache = {};
    
    function tryModuleLoad(module) {
        let extension = path.extname(module.id);
        Module._extensions[extension](module);
    }
    
    
    function req(modulePath){
        // 1、路径解析
        let absModulePath = path.resolve(__dirname, modulePath);
    
        // 4、对模块添加后缀名
        let newPath = absModulePath;
        let extNames = Object.keys(Module._extensions);
        let index = 0;
        while(index < extNames.length){
            try{
                fs.accessSync(newPath);
                break;
            }catch(e){
                newPath = absModulePath + extNames[index++];
            }
        }
    
        try{
            fs.accessSync(newPath);
        }catch(e){
            throw new Error('The file you required does not exist!');
        }
    
        // 5、对模块进行缓存添加
        if (Module._cache[newPath]){
            return Module._cache[newPath].exports;
        }
    
        // 2、 创建模块
        let module = new Module(newPath);
        // 5、对模块进行缓存添加
        Module._cache[newPath] = module;
        // 3、 尝试加载该模块
        tryModuleLoad(module);
        return module.exports
    }
    
    let r = req('./a.js');    // 策略: 默认会增加.js    .json
    req('./a.js');
    console.log(r);

你可能感兴趣的:(commonJs-模块引入require的简单实现)