当前位置: 首页 > 后端技术 > Node.js

Node简单解释(二)Module机制

时间:2023-04-03 13:19:40 Node.js

Module机制1.CommonJS的背景js实现编写后端程序的不足1.没有模块体系(js的先天不足就是模块功能)2.ECMAScript只定义了js的核心库,而对于文件系统、IO系统等并没有标准的API。虽然HTML5一直致力于推动标准化,但这些标准都是前端的。3.没有标准接口,没有定义服务器或数据库接口。4.缺少包管理系统,无法自动安装和管理依赖项。CommonJS的出现正好弥补了没有标准的缺憾。CommonJS对模块、二进制、Buffer、二进制、I/0、进程环境、文件系统、web服务器网络管理接口、包管理等进行了标准化。2.CommonJS与Node的关系Node的出现离不开CommonJS规范,而CommonJS能以不同寻常的姿态出现在各大公司的代码中,这与Node.js的优秀性能是分不开的。三、CommonJS模块规范1.Moduleimportvarhttp=require("http");2.模块导出//math.jsexports.add=function(){...}//program.jsVarmath=require('math.js');exports.increament=function(val){returnmath.add(val);}3.模块ID模块ID是require()中括号内的参数,必须是小驼峰结构,可以是相对路径,也可以是绝对路径,也可以没有后缀。4.Node的模块实现Node的模块实现其实是借鉴了CommonJS的部分,并没有全部照搬,还增加了一些自己需要的东西。一、模块导入模块在node中导入需要经过:1.路径解析2.文件定位3.编译执行2.模块分类1.Node提供的核心模块2.用户编写的文件模块3.核心模块核心module部分在Node源码加载时编译完成,编译成二进制执行文件。Node进程启动后,一些核心模块直接加载到内存中,不需要`文件定位`和`编译`,所以加载一些核心模块时速度最快。4.文件模块文件模块是在运行时动态加载的。需要经过完整的路径解析、文件定位、编译执行,一般慢一次。5.缓冲加载Node对两次加载的核心模块始终采用先缓冲的原则。6.路径分析和文件位置模块标识符分析核心模块核心模块的加载仅次于缓冲。加载路径形式的文件模块首先转换为真实路径,然后以真实路径为索引编译放入缓冲区,等待电话。因为是通过准确的文件地址找到的,所以需要一定的时间。自定义模块是非核心模块,不是路径形式的标识。它是一个特殊的文件模块,可以是一个文件,也可以是一个包。这种查找是最耗时的。首先你要知道/modulepath的概念//>比如你要加载一个包,这个包放在node_modules文件夹下。如果要导入,可以不写路径形式,直接写名字即可。(也就是引入一个自己npm的包)console.log(module.paths);//会得到如下数组['D:\\myweb\\node\\node\\module\\node_modules','D:\\myweb\\node\\node\\node_modules','D:\\myweb\\node\\node_modules','D:\\myweb\\node_modules','D:\\node_modules']这个为module设置的路径,查找机制如下:首先检查当前路径下是否有node_modules文件夹下的包,如果没有则查找上层目录,依此类推,直到找到该路径下的node_modules根目录,如果还是找不到,那就报错上去。//很明显这种情况是其速度慢的主要原因,(搜索路径越深越慢),但是我们可以通过一些小技巧尽可能的减少这种情况~~filepositioningfilepositioning也是需要注意的包括文件扩展名、目录和包的处理。node的module导入的时候不需要写extension,node会按照jsjsonnode的顺序解析。依次尝试。由于你是尝试在node中使用fs模块的同步文件查找,可能会造成阻塞,所以这里需要注意两个提示:提示:1.json节点文件最好加上扩展名2.SynchronizationBuffering可以解决Node单线程阻塞调用的缺陷。另外,如果没有找到对应的文件,但是确实找到了一个目录,也会被当做一个包。如何在这个包下找到我们需要导入的入口文件对呢?1、首先查看是否包含package.json,如果包含则分析其main属性,找到main属性对应的文件。2.如果没有package.json或者主解析失败,则找到名为index的文件,从index.jsindex.jsonindex.node依次查找。3.如果在这个目录下还是找不到,那就搜索并写一个匹配的目录,如果还是找不到,那么就会报错。6.模块编译node中的每个模块都是一个对象。当定位到一个文件后,node会把它打包成一个module对象,然后根据不同的文件名,它的加载方式也不同。js文件:通过fs同步读取文件执行json文件,通过fs同步读取文件,使用Json.parse()获取对象,赋值给Module.exportsnode文件,该文件是通过fs编写的扩展文件c/c++,通过Process.dlopen()方法加载执行。(不需要编译)7.module.exports和exports的区别和连接exports=module.exports={};区别与vara={};之间的区别相同。b=一个;是一个对象{Object}。当你新建一个文件,比如mo.js,文件内容如下:console.log(module);然后在CMD中执行这个文件nodemo.js,可以看到module其实就是一个Module实例,可以这样理解,NodeJS中定义了一个Module类。这个类中有很多属性和方法,exports是属性之一:模块实例。varmodule=newModule();console.log(module);//你会看到Module中的导出是空对象{}module.exports={print:function(){console.log(12345)}}console.log(module);//你会看到Module中的exports对象已经有了print()方法module.exports其实就是模块实例中的模块添加方法或属性。控制台日志(模块);//你会看到Module中的exports都是空对象{}console.log(exports);//你会看到Module中的导出是空对象{}module.exports={print:function(){console.log(12345)}}console.log(module);//你会看到Module中的exports对象有个print()方法exports.name='小白姐姐';console.log(module);//你会看到Module中的exports对象不仅有print()方法,还有name属性。不难看出exports其实是对module.exports的引用。//你可以这样理解varmodule=newModule();varexports=module.exports;当你需要时,返回的是module.exports的内容。//常见场景分析module.exports.name='小白姐姐';exports.age=10;module.exports.print=function(){console.log(12345)};//如果只是添加属性和方法,两者可以混用。//也可用module.exports={name='小白姐姐';};exports.age=10;module.exports.print=function(){console.log(12345)};//[X]但不是你可以module.exports={name='小白姐姐';};exports={age:10};//exports现在是对对象{age:10}的引用,不再是对module.exports控制台的引用。日志(模块);//你会看到Module的exports中只有name属性!!!//【X】exports.age=10;控制台日志(模块);//你会看到在Module的exports中添加了age属性覆盖。控制台日志(模块);//你会看到Module的exports中仍然只有name属性!!!总结修改导出方向后添加的exports.xxx无效。因为require只返回module.exports,使用exports.xxx后不能改变module.exports的指向。因为exports.xxx添加的属性和方法在module.exports指向的新对象中是不存在的。对于需要导出的属性,直接挂在exports对象上即可。对于类,为了直接使用导出的内容作为类的构造函数,调用者可以使用new操作符创建实例对象,应该把构造函数挂在上面的module.exports对象上,不要和exporting混淆属性值