模擬node.js中require的加載機制

一、先了解一下,nodejs中require的加載機制

1、require的加載文件順序

require 加載文件時可以省略擴展名:

require('./module');

此時文件按 JS 文件執行:

require('./module.js');

此時文件按 JSON 文件解析:

require('./module.json');

此時文件預編譯好的 C++ 模塊執行

require('./module.node');

載入目錄module目錄中的 package.json 中main指向的文件

require('./module/default.js');

載入目錄module 中的index.js文件
通過 ./ 或 …/ 開頭:則按照相對路徑從當前文件所在文件夾開始尋找模塊;

require('../file.js');  => 上級目錄下找 file.js 文件

通過 / 開頭:則以系統根目錄開始尋找模塊;

require('/Users/iceStone/Documents/file.js'); => 以絕對路徑的方式找,沒有任何異議

如果參數字符串不以“./“ 或 ”/“ 開頭,則表示加載的是一個默認提供的核心模塊(位於 Node 的系統安裝目錄中):

require('fs'); => 加載核心模塊中的文件系統模塊

或者從當前目錄向上搜索 node_modules 目錄中的文件:

require('my_module'); => 各級 node_modules 文件夾中搜索 my_module.js 文件;

如果 require 傳入的是一個目錄的路徑,會自動查看該目錄的 package.json 文件,然後加載 main 字段指定的入口文件
如果package.json文件沒有main字段,或者根本就沒有package.json文件,則默認找目錄下的 index.js 文件作爲模塊:

require('./calcuator'); => 當前目錄下找 calculator 目錄中的 index.js 文件

2、require緩存

第一次加載某個模塊時,Node 會緩存該模塊。以後再加載該模塊,就直接從緩存取出該模塊的 module.exports 屬性(不會再次執行該模塊)
如果需要多次執行模塊中的代碼,一般可以讓模塊暴露行爲(函數),模塊的緩存可以通過 require.cache 拿到,同樣也可以刪除delete require.cache[require.resolve('模塊名')];

3、所有代碼都運行在模塊作用域,不會污染全局作用域。

// nodejs_require.js
// [require函數模擬]

"use strict"

function $require(id) {
    //1、先查看模塊是否存在,如果不存在則拋出 Can't found file
    //2、如果存在,就讀取代碼
    const fs = require('fs');
    const path = require('path');

    // 文件名
    let filename = id;
    //查看是否是絕對路徑
    if (!path.isAbsolute(filename)) {
        filename = path.join(__dirname, id);
    }

    // 文件目錄
    let dirname = path.dirname(filename);

    //模擬require的緩存機制
    $require.cache = $require.cache || {};
    // 註釋掉 一下代碼, 每次加載不適用緩存數據, 每次加載模塊讀取緩存,放開下面的代碼
    /*if($require.cache[filename]){
        return $require.cache[filename].exports;
    }*/

    // 讀取模塊代碼
    let srccode="";
    try {
        srccode = fs.readFileSync(filename,'utf-8'); // 阻塞讀取文件,不會放入事件隊列
    } catch (error) {
        console.log(" [*]can't found file! ");
        throw error;
    }

    // console.log(code);

    // 3、執行代碼,所要執行的代碼,需要營造一個獨立的空間

    // 定義一個數據容器,用容器去裝模塊導出的成員
    let _module = { // 每個js模塊都會有一個全局的module變量
        id:'.',
        exports:{},
        parent:module,
        filename:filename
    };
    let _exports = _module.exports;

    // 營造一個獨立空間
    let code = `(function($require,module,_exports,__dirname,__filename){
 ${ srccode }
})($require,_module,_exports,dirname,filename)`;

    // 執行代碼
    eval(code);

    // 緩存
    $require.cache[filename] = _module;
    // 返回結果
    return _module.exports;
}


setTimeout(()=>{
    const rr = $require("./test.js");
    console.log(rr);

	// 斷點調試,修改文件後,繼續向下執行,  導入內容發生改變

    const rr1 = $require("./test.js");
    console.log(rr1);

},1000);

Good Luck~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章