看穿node中的Module

Meteor發表於2018-04-11

為了減少變數汙染以及程式碼維護node採用了CommonJS規範。今天模擬一下Module。

  1. 首先建立被引入檔案 a.js
module.exports = 'hello';
複製程式碼
  1. 建立Module檔案
//定義Module類
class Module{
    constructor(filename){
        this.filename = filename;
        this.exports = {};
    }
}
//定義require方法
function myRequire(){
    
}
let resut = myRequire('./a');
複製程式碼
  1. 獲取檔案路徑
function myRequire(filename){
    //獲取檔案絕對路徑
    filename = Module.getPath(filename);
}
class Module{
    ...
    //Module下定義靜態方法
    static getPath(filename){
        filename = path.join(__dirname,filename);
        let ext = path.extname(filename);
        let pathName = '';
        let error = filename + " is not exist";
        //判斷檔案是否存在擴充名
        if(ext){
            try{
                fs.accessSync(filename);
                return filename;
            }catch(e){
                throw error;
            }
        }

        for(var i=0; i<Module.extentions.length; i++){
        //給檔案加上擴充名,查詢檔案是否存在
            pathName = filename + Module.extentions[i];
            try{
                fs.accessSync(pathName);
                return pathName;
            }catch(e){}
        }
        throw error;
    }
}
//定義擴充名
Module.extentions = ['.js','.json','.node'];
複製程式碼
  1. 讀取檔案
class Module{
    ...
     load(){
        let ext = path.extname(this.filename).slice(1);
        //根據檔案型別讀取檔案
        Module.extentions[ext](this);
    }
    static wrap(script){
        return Module.warper[0] + script + Module.warper[1];
    }
}
Module.extentions["js"] = function(module){
    let script = fs.readFileSync(module.filename,'utf8');
    let fnStr = Module.wrap(script);
    //(function(exports,require,module,__dirname,__filename){module.exports = 'hello'})
    
    let dirname = path.resolve(module.filename,'../');
    vm.runInThisContext(fnStr).call(module.exports,module.exports,myRequire,module,dirname,module.filename);
    //檔案內部會獲得傳過來的module物件
    //模組內部將要匯出的檔案掛載到module.exports上
    //module.exports = 'hello'
}
Module.extentions["json"] = function(module){
    let script = fs.readFileSync(module.filename,'utf8');
    module.exports = JSON.parse(script);
}
//用於拼接巢狀js檔案
Module.warper = ["(function(exports,require,module,__dirname,__filename){",    "\n})"];
function myRequire(filename){
    ...
    let module = new Module(filename);
    module.load();
}
複製程式碼
  1. 暫存已讀取檔案
function myRequire(filename){
    let fileCache = Module.caches[filename];
    //有快取讀取快取
    if(fileCache){
        return fileCache.exports;
    }
    //載入檔案
    let module = new Module(filename);
    module.load();
    //快取檔案
    Module.caches[filename] = module;
}
複製程式碼
  1. 完整程式碼

a.js

module.exports = 'hello'
複製程式碼

module.js

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


class Module{
    constructor(filename){
        this.filename = filename;
        this.exports = {};
    }
    load(){
        let ext = path.extname(this.filename).slice(1);
        Module.extentions[ext](this);
    }
    static wrap(script){
        return Module.warper[0] + script + Module.warper[1];
    }
    static getPath(filename){
        filename = path.join(__dirname,filename);
        let ext = path.extname(filename);
        let pathName = '';
        let error = filename + " is not exist";
        if(ext){
            try{
                fs.accessSync(filename);
                return filename;
            }catch(e){
                throw error;
            }
        }

        for(var i=0; i<Module.extentions.length; i++){
            pathName = filename + Module.extentions[i];
            try{
                fs.accessSync(pathName);
                return pathName;
            }catch(e){}
        }
        throw error;
        // path.resolve(__dirname, filename);
    }
}
Module.extentions = ['.js','.json','.node'];
Module.caches = {};
Module.warper = ["(function(exports,require,module,__dirname,__filename){",    "\n})"];
Module.extentions["js"] = function(module){
    let script = fs.readFileSync(module.filename,'utf8');
    let fnStr = Module.wrap(script);
    let dirname = path.resolve(module.filename,'../');
    vm.runInThisContext(fnStr).call(module.exports,module.exports,myRequire,module,dirname,module.filename);
}
Module.extentions["json"] = function(module){
    let script = fs.readFileSync(module.filename,'utf8');
    module.exports = script;
}

function myRequire(filename){
    //查詢檔案絕對路徑
    filename = Module.getPath(filename);
    let fileCache = Module.caches[filename];
    if(fileCache){
        return fileCache.exports;
    }
    //載入檔案
    let module = new Module(filename);
    module.load();
    Module.caches[filename] = module;
    return module.exports;
}

let school = myRequire('./a.js');

複製程式碼

歡迎大家指出問題提出見解!

相關文章