Node 实现 require 加载模块

require 实现原理 mini 版


const fs = require('fs');
const path = require('path');
const vm = require('vm');

// require模块查找文件规范
// 1. 核心模块、内置模块、node自带模块
// 2. 文件模块,引用都是相对路径
// node最新版本: 默认先查找同名文件,如果没有找到则尝试查找.js或者.json文件,如果仍然没有就查找到同名文件夹(会当成一个包)
// 先查找package.json中的main指定文件,没有就继续查找index.js,如果扔未找到则报错
// node老版本: 默认先查找package.json中的main,如果没有package.json 则会继续查找文件(已废弃)
// 3.第三方模块(安装包中都有描述信息,否则无法上传)(引用也是没有相对路径,1. 全局模块 2. 代码中的第三方模块)
// 默认会沿着当前目录向上查找,查找node_modules中的同名文件夹,根据package.json中的main进行查找 -> index.js,
// 如果还未找到则继续向上查找,查找上级node_modules目录,一直查找到根路径,如果仍未找到则会报错


// node的调试方法
// 1. 直接在vscode中调试
// 2. 可以在chrome中调试 方案调试 node --inspect-brk  执行文件

// require 执行流程
// 1. require方法 -> Module.prototype.require() 方法
// 2. Module._load() 加载模块
// 3. Module._resolveFilename() 方法就是把路径变成了绝对中午,添加后缀(.js,.json) .node
// 4. new Module() 拿到绝对路径创建一个模块 this.id exports = {}
// 5. module.load() 对模块进行加载
// 6. 根据文件后缀 Module._extensions['.js'] 去做策略加载
// 7. 用的是同步读取文件
// 8. 增加一个函数的壳子,并且让函数执行,让module.exports做为this
// 9. 用户默认会拿到module.exports的返回结果
// 10. 最终返回exports对象
function Module(id) {
  this.id = id; // 存放路径 
  this.exports = {};
}
// module 缓存
Module._cache = {};
Module._extensions = {
  '.json'(module) {
    let scripts = fs.readFileSync(module.id, 'utf-8');
    module.exports = JSON.parse(scripts);
  },
  '.js'(module) {
    let script = fs.readFileSync(module.id, 'utf-8');
    let templateFn = `(function(exports,module,require,__filename,__dirname){${script}})`;
    // 用vm虚拟机运行templateFn代码,runInThisContext()无法获取本地作用域,所以不会污染本地作用域。跟eval具体差别查看 [此处](http://nodejs.cn/api/vm/vm_runinthiscontext_code_options.html)
    let fn = vm.runInThisContext(templateFn);
    let exports = module.exports;
    let thisValue = exports;
    let filename = module.id;
    let dirname = path.dirname(filename);
    fn.call(thisValue,exports,module,req,filename,dirname);
  }
}
Module._resolveFilename = function (id) {
  let filepath = path.resolve(__dirname, id);
  let isExists = fs.statSync(filepath);
  if (isExists) return filepath;
  let keys = Object.keys(Module._extensions);
  for (let i = 0; i < keys.length; i++) {
    let newPath = filepath + keys[i];
    if (fs.statSync(newPath)) return newPath;
  }
  throw new Error("module not found!");
}
Module.prototype.load = function () {
  let extname = path.extname(this.id);
  Module._extensions[extname](this);
}
function req(filename) {
  filename = Module._resolveFilename(filename); //1. 创造一个绝对引用地址
  let cacheModule = Module._cache[filename];
  if (cacheModule) return cacheModule.exports;  // 如果module有缓存,就返回缓存,防止多次引用执行
  const module = new Module(filename); //1. 根据路径创造一个模块
  Module._cache[filename] = module;  // 对module进行缓存 
  module.load();  // 让用户给module.exports赋值

  return module.exports; // 默念是空对象
}

const test = req('./test.js');
// 对文件加载进行缓存之后,则不会进行重复加载.
req('./test.js')
req('./test.js')
console.log(test);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值