nodeJS模塊化,使用commonJS規范,該規范以讀取文件實現模塊化。
(function(exports,require, module, __filename,__dirname) { module.export = XXX; return module.exports; })
1. commonJS規范:
1. 文件即模塊。(讀取的文件是字符串)
2. 定義了導出文件的方式module.exports 和 exports
3. 定義了引入文件的方式require
**瀏覽器中讓字符串運行**
eval / new Function
**nodeJS中字符串運行**
const vm = require('vm'); vm.runInThisContext(str);
2. require方法的實現
* 內部實現了一個內置的require方法 * 使用`Module._load`方法加載模塊 * 使用`Module.__resolveFilename`,將相對路徑=>絕對路徑+文件后綴 * 緩存機制`Module._cache`,緩存模塊 * 新建一個模塊 `new Module`;Module有兩個主要屬性id(路徑), exports={} * 使用`tryModuleLoad`嘗試加載模塊 * 獲取文件后綴 * 通過`Module._extensions`上后綴對應的方法加載模塊->讀取文件 * `Module.wrap`包裹讀取的字符串內容; * `runInThisContext`運行包裹后的字符串;將字符串轉為函數 * 運行函數,並將this.exports(默認空對象)作為this綁定到函數的this上
let path = require('path');
let fs = require('fs');
let vm = require('vm');
function Module(path){
this.id = path;
this.exports = {};
}
Module.wrapper = [
'(function(exports, require, module, __filename,__dirname){',
'})'
]
Module.extensions = {
'.js': function(module) {
let content = fs.readFileSync(module.id, 'utf8');
let fnStr = Module.wrapper[0] + content + Module.wrapper[1];
let wrapperFn = vm.runInThisContext(fnStr);
wrapperFn.call(module.exports, module.exports, req, module, __filename, __dirname);
// 該方法是用戶自定義給module.exports賦值;
},
'.json': function(module) {
let json = fs.readFileSync(module.id, 'utf8');
module.exports = json;
},
'.node': {
//
}
}
function tryModuleLoad(module) {
let extension = path.extname(module.id);
Module.extensions[extension](module);
}
Module._cache = {};
function req(modulePath) {
let resolvedPath = path.resolve(modulePath);
// 如果路徑的文件未寫擴展名;需要按照默認擴展名依次查找
let i = 0;
function findFilePath(parsePath) {
try {
fs.accessSync(parsePath);
return parsePath;
} catch(e){
let extensions = Object.keys(Module.extensions);
let tempPath= resolvedPath + extensions[i++];
return findFilePath(tempPath);
}
}
let absolutePath = findFilePath(resolvedPath);
if(Module._cache[absolutePath]) {
return Module._cache[absolutePath].exports;
}
const module = new Module(absolutePath);
tryModuleLoad(module);
Module._cache[absolutePath] = module;
return module.exports;
}
console.log(req('./1'));
3. module.exports和exports的區別
1. exports是module.exports的別名,兩者指向地址相同;
2. 但是模塊導出的是module.exports。如果使用`exports = xxxx`導出,則exports地址修改。
而module.exports初始值是{},則模塊最終導出的是{}
3. 如果一定要用exports,則可以通過給exports對象添加屬性,則相當於同時給module.exports添加。
如:`exports.a = xxx;`
最終的導出結果是`{a: xxx}`
