为中华民族的伟大复兴而读书。
前情回顾
上篇文章主要分享了的一个commonJS规范
的问题,那么今天接着昨天的话题继续聊一聊nodejs的模块儿查找机制
Node模块儿
在Node中引入模块,大致会经历这么几个过程:
路径分析
文件定位
编译执行
在Node中,模块儿可以分为两大类,一类是Node提供的模块成为核心模块
;另一类是用户编写的模块,成为文件模块
。
核心模块在Node源码编译的过程中,编译进了二进制执行文件中。当Node进程启动时,核心模块儿会直接被加载到内存中,所以核心模块引入时,文件定位和编译执行这两个步骤可以忽略掉,并且在路径分析中会优先判断,所以核心模块的加载速度是最快的
。
文件模块则是在运行中动态加载,需要完整的路径分析,文件定位,编译执行过程,加载速度比核心模块会慢一些。
模块儿加载过程
优先从缓存中加载
。
Node对引入过的文件都会进行缓存,用来减少二次引用时的开销。与浏览器缓存不同的是,浏览器缓存的是文件,Node缓存的是编译和执行后的对象。
不论是核心模块还是文件模块,相同模块儿的二次加载都采用缓存优先的方式
。但是核心模块的缓存检查优先级高于文件模块的缓存检查
。
路径分析和文件定位
不同的标识符,模块的查找及定位也有不同程度的差异。
核心模块
的优先级仅次于缓存加载,它在Node源码中已经编译成二进制代码,所以其加载速度最快。绝对路径或相对路径
这类标识符通常是我们自己写的文件模块儿,require()
方法会将路径转为真实路径,并且以真实路径作为索引,将编译后的结果存放到缓存中,方便下载加载时更快。自定义模块儿 或者可以理解为npm包
。这类模块查找是最慢的,具体过程是这样的:当前目录的node_modules文件夹
-->父目录下的node_modules文件夹
-->递归查找父目录的node_modules
-->直到根目录下的node_modules
这过程跟JS的作用域链有些类似,但是文件层级越深,查找越耗时。核心模块儿如 http,fs,path...
绝对路径或相对路径
自定义模块儿 或者可以理解为npm包
模块标识符分析
- 优先从缓存加载的策略时二次加载的效率得到了很大的提升,但是还需要考虑一些别的细节,比如:
文件扩展名
,目录的处理
,包的处理
等等 默认require()
方法中的标识符不带扩展名,这种情况下Node会按照.js
,.json
,.node
的顺序依次补足扩展名,依次尝试。 这个过程中fs
模块儿会同步阻塞式的判断文件是否存在。因为Node是单线程,所以会有一些性能上的问题,所以如果是json
文件,带上扩展名,会多少提高一些性能。 在分析标识符的过程中,require()
通过分析扩展名之后,可能没有查到对应文件,但是却得到一个目录,此时Node会将这个目录当做一个包来处理。然后查找package.json
,通过JSON.parse()
解析出描述对象,然后从main
属性指定的文件进行定位查找。没有扩展名则补齐扩展名,如果整个过程全部走完依然没有找到目标文件,则抛出异常。文件定位
下一篇简单聊一下模块如何编译及npm的相关知识点
总结
优先从缓存加载
递归查找
javascript基础知识总结