前端模块化Commonjs的基本原理实现

192 阅读2分钟

1.前端模块化Commonjs的基本原理实现

// 在使用require的时候,他是一个函数,接收你引用的本地文件名,
// 然后通过Module._load方法去加载文件,
// 内部通过_resolveFileName将相对路径转换成 绝对路径,
// -----------------------------------------  注意:不能使用相对文件路径,因为对应文件不明确,可能会发生文件加载不到问题,

// 内部有几个方法,cache缓存,判断这个文件是否已经加载过,loadNativeModule判断这个文件是否为原生模块,看传进来的模块名称是否为绝路径
// 创建一个模块,有一个id和exports 文件路径和导出结果,并且会将模块缓存起来
// 最后在真正的加载这个模块。

// 内部有一个方法叫extensions方法,会根据文件的后缀名来解析文件, 调用对应的处理方法。
// 最终返回是一个exports对象 


const path = require('path');
const fs = require('fs')
const vm = require('vm')
function Module(id){
    this.id = id
    this.exports = {}
}
// 策略模式,对应的文件处理不同的逻辑
Module._extensions = {
    '.js':function(module){
        // 读取js文件中的源代码
        let sourceJson = fs.readFileSync(module.id)
        let wrapper = ['(function(exports,module,require,_dirname,_filename){','})']
        // 拼接成一个函数
        const script = wrapper[0] + sourceJson + wrapper[1]
        console.log(script);
        // 调用vm.runInThisContext变成真正可执行的函数
        let fn = vm.runInThisContext(script);
        let exports = module.exports;
        let dirname = path.dirname(module.id)
        // 当用户调用module.exports的时候会自动把值返回出去
        fn.call(exports,exports,module,req,dirname,module.id)
    },
    '.json':function(module){
        console.log(module);
        // 最后读取.json文件,contentJson就会json的内容,转换成一个对象
        let contentJson = fs.readFileSync(module.id)
        module.exports = JSON.parse(contentJson);
    }
}
// 拿到文件的绝对路径
Module._resolveFileName = function(id){
    let filePath = path.resolve(id) // 通过resolve方法拿到文件的绝对路径
    //判断文件是否存在
    if( fs.existsSync(filePath) ){
        return filePath
    }
    // 如果获取不到则依次去追加后缀名看是否能获取到
    let exts = Object.keys(Module._extensions);
    for (let index = 0; index < exts.length; index++) {
        let p = filePath+exts[index] //依次拼接后缀名
        if( fs.existsSync(p) ){
            return filePath;
        }
    }
    // 如果还获取不到则抛出异常
    throw new Error('模块不存在~')
}
// 全局的缓存区
Module._cache = {

}
// 加载文件模块方法
Module.prototype._load = function(id){
    let extensions = path.extname(this.id); //获取当前文件的扩展名
    Module._extensions[extensions](this)
}
function req(id){
    // 将路径装换成绝对路径, 可能id没有后缀,需要依次尝试追加后缀.js .json
    let filePath = Module._resolveFileName(id);
    // 如果缓存中有这个模块,则使用上次缓存的结果
    if( Module._cache[filePath] ){
        return Module._cache[filePath].exports;
    }
    // 通过构造函数去创建一个模块
    let module = new Module(filePath)
    // 将模块进行缓存,禁止多次解析文件
    Module._cache[filePath] = module;
    // 真正加载文件模块
    module._load(filePath)
    // 最终导出结果
    return module.exports;
}

let r = req('a.js')
r = req('a.js')
r = req('a.js')
console.log(r);