简单模拟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对象的比较
Feb 26 Javascript
JQuery onload、ready概念介绍及使用方法
Apr 27 Javascript
IE与FireFox的JavaScript兼容问题解决办法
Dec 31 Javascript
jquery.post用法之type设置问题
Feb 24 Javascript
jQuery的:parent选择器定义和用法
Jul 01 Javascript
javascript学习笔记(三)BOM和DOM详解
Sep 30 Javascript
把普通对象转换成json格式的对象的简单实例
Jul 04 Javascript
Jquery on绑定的事件 触发多次实例代码
Dec 08 Javascript
jQuery导航条固定定位效果实例代码
May 26 jQuery
jQuery:unbind方法的使用详解
Aug 14 jQuery
React 组件间的通信示例
Jun 14 Javascript
手动下载Chrome并解决puppeteer无法使用问题
Nov 12 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
cache_lite试用
2007/02/14 PHP
实用函数5
2007/11/08 PHP
yii实现级联下拉菜单的方法
2014/07/31 PHP
PHP遍历文件夹与文件类及处理类用法实例
2014/09/23 PHP
Laravel框架实现的上传图片到七牛功能详解
2019/09/06 PHP
鼠标移动到一张图片时变为另一张图片
2006/12/05 Javascript
关于innerHTML后丢失动态绑定的EVENT问题解决方法
2013/05/19 Javascript
一个JavaScript递归实现反转数组字符串的实例
2014/10/14 Javascript
jQuery基于Ajax方式提交表单功能示例
2017/02/10 Javascript
javascript自执行函数
2017/02/10 Javascript
深入理解vue中的$set
2017/06/01 Javascript
微信小程序登录换取token的教程
2018/05/31 Javascript
React实现全局组件的Toast轻提示效果
2018/09/21 Javascript
vue-cli中安装方法(图文详细步骤)
2018/12/12 Javascript
vue缓存的keepalive页面刷新数据的方法
2019/04/23 Javascript
layer.js open 隐藏滚动条的例子
2019/09/05 Javascript
vue中的循环对象属性和属性值用法
2020/09/04 Javascript
vue 动态组件(component :is) 和 dom元素限制(is)用法说明
2020/09/04 Javascript
python对指定目录下文件进行批量重命名的方法
2015/04/18 Python
PyTorch 1.0 正式版已经发布了
2018/12/13 Python
python 多线程重启方法
2019/02/18 Python
PyTorch基本数据类型(一)
2019/05/22 Python
如何使用pyinstaller打包32位的exe程序
2019/05/26 Python
详解Numpy中的数组拼接、合并操作(concatenate, append, stack, hstack, vstack, r_, c_等)
2019/05/27 Python
python异步实现定时任务和周期任务的方法
2019/06/29 Python
django-orm F对象的使用 按照两个字段的和,乘积排序实例
2020/05/18 Python
前端隐藏出边界内容的实现方法
2016/04/14 HTML / CSS
亚历山大·王官网:Alexander Wang
2017/06/23 全球购物
新闻系毕业生推荐信
2013/11/16 职场文书
九年级物理教学反思
2014/01/29 职场文书
代理人委托书
2014/09/16 职场文书
财务人员个人工作总结
2015/02/27 职场文书
宇宙与人观后感
2015/06/05 职场文书
瞿秋白纪念馆观后感
2015/06/10 职场文书
JavaScript实现淘宝商品图切换效果
2021/04/29 Javascript
golang实现一个简单的websocket聊天室功能
2021/10/05 Golang