简单模拟node.js中require的加载机制


Posted in Javascript onOctober 27, 2016

一、先了解一下,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 拿到,同样也可以删除

3、所有代码都运行在模块作用域,不会污染全局作用域。

二、模拟require函数

require的加载内部比较复杂,下面让我们进行简单的模拟加载

require的简单实现机制为:

     将传入的模块id通过加载规则找到对应的模块文件

     读取这个文件里面的代码

     通过拼接方式为该段代码构建私有空间

     执行该代码

     拿到module.exports 返回

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 code="";
 try {
  code = 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;

 // 营造一个独立空间
 code = `(function($require,module,_exports,__dirname,__filename){
  $[code]
 })($require,_module,_exports,dirname,filename)`;

 // 执行代码
 eval(code);

 // 缓存
 $require.cache[filename] = _module;
 // 返回结果
 return _module.exports;
 }


 setInterval(()=>{
  const rr = $require("./test.js");
  console.log(rr);
 },1000);

上面的模块测试使用的两个模块

//test.js
 const date = $require('./date.js');

 console.log(module);

 module.exports = date;

 //date.js
 module.exports = new Date();

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
js刷新框架子页面的七种方法代码
Nov 20 Javascript
基于jquery的兼容各种浏览器的iframe自适应高度的脚本
Aug 13 Javascript
ExtJs Excel导出并下载IIS服务器端遇到的问题
Sep 16 Javascript
控制文字内容的显示与隐藏示例
Jun 11 Javascript
AngularJs Javascript MVC 框架
Jun 20 Javascript
基于JavaScript实现的快速排序算法分析
Apr 14 Javascript
redux中间件之redux-thunk的具体使用
Apr 17 Javascript
layui实现checkbox的目录树tree的例子
Sep 12 Javascript
请求时token过期自动刷新token操作
Sep 11 Javascript
微信小程序实现星星评分效果
Nov 01 Javascript
vue-router路由懒加载及实现的3种方式
Feb 28 Vue.js
JavaScript实现复选框全选功能
Apr 11 Javascript
浅谈jquery中next与siblings的区别
Oct 27 #Javascript
vue多级多选菜单组件开发
Sep 08 #Javascript
使用Javascript监控前端相关数据的代码
Oct 27 #Javascript
js实现各种复制到剪贴板的方法(分享)
Oct 27 #Javascript
js复制内容到剪贴板代码,js复制代码的简单实例
Oct 27 #Javascript
微信小程序 定义全局数据、函数复用、模版等详细介绍
Oct 27 #Javascript
vue插件tab选项卡使用小结
Oct 27 #Javascript
You might like
PHP中3种生成XML文件方法的速度效率比较
2012/10/06 PHP
解析使用substr截取UTF-8中文字符串出现乱码的问题
2013/06/20 PHP
php对象在内存中的存在形式分析
2015/02/03 PHP
php中 $$str 中 "$$" 的详解
2015/07/06 PHP
php自定义分页类完整实例
2015/12/25 PHP
javascript是怎么继承的介绍
2012/01/05 Javascript
jQuery ReferenceError: $ is not defined 错误的处理办法
2013/05/10 Javascript
将HTML的左右尖括号等转义成实体形式的两种实现方式
2014/05/04 Javascript
jQuery插件ajaxFileUpload实现异步上传文件效果
2015/04/14 Javascript
javascript中undefined与null的区别
2015/08/16 Javascript
Javascript实现跑马灯效果的简单实例
2016/05/31 Javascript
Bootstrap导航条可点击和鼠标悬停显示下拉菜单的实现代码
2016/06/23 Javascript
微信小程序开发之视频播放器 Video 弹幕 弹幕颜色自定义实例
2016/12/08 Javascript
详解在vue-test-utils中mock全局对象
2018/11/07 Javascript
微信小程序select下拉框实现效果
2019/05/15 Javascript
layui实现三级导航菜单
2019/07/26 Javascript
vue 框架下自定义滚动条(easyscroll)实现方法
2019/08/29 Javascript
JavaScript鼠标悬停事件用法解析
2020/05/15 Javascript
Python字符串、整数、和浮点型数相互转换实例
2018/08/04 Python
python3对拉勾数据进行可视化分析的方法详解
2019/04/03 Python
浅析Python 实现一个自动化翻译和替换的工具
2019/04/14 Python
python关于倒排列的知识点总结
2020/10/13 Python
h5封装下拉刷新
2020/08/25 HTML / CSS
深深扎根运动世界的生活品牌:Tillys
2017/10/30 全球购物
英国自行车商店:AW Cycles
2021/02/24 全球购物
军训自我鉴定范文
2014/02/13 职场文书
党员干部对十八届四中全会的期盼
2014/10/17 职场文书
如何写辞职书
2015/02/26 职场文书
2015暑假打工实践报告
2015/07/13 职场文书
学术会议开幕词
2016/03/03 职场文书
如何撰写出一份完美的商业计划书?
2019/07/12 职场文书
《哪吒之魔童降世》观后感:世上哪有随随便便的成功
2019/11/08 职场文书
理解深度学习之深度学习简介
2021/04/14 Python
Python编解码问题及文本文件处理方法详解
2021/06/20 Python
golang生成并解析JSON
2022/04/14 Golang
GO语言字符串处理函数之处理Strings包
2022/04/14 Golang