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