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}`