一、先了解一下,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);