
今天的这篇经验主要给大家介绍深入理解Commonjs规范及Node模块实现,相信理解了这些会对开发百度小程序有着不小的帮助。Node在实现中并非完全按照CommonJS规范实现,而是对模块规范进行了一定的取舍,同时也增加了少许自身需要的特性。
缓存加载。
在javascript中,加载模块使用script标签即可,而在nodejs中,如何在一个模块中,加载另一个模块呢?
不论是核心模块还是文件模块,require()方法对相同模块的二次加载都一律采用缓存优先的方式,这是第一优先级的。不同之处在于核心模块的缓存检查先于文件模块的缓存检查。
标识符分析。
require()方法接受一个标识符作为参数。在Node实现中,正是基于这样一个标识符进行模块查找的。模块标识符在Node中主要分为以下几类:
核心模块,如http、fs、path等;
.或..开始的相对路径文件模块;
以/开始的绝对路径文件模块;
非路径形式的文件模块,如自定义的connect模块。
文件扩展名分析。
require()在分析标识符的过程中,会出现标识符中不包含文件扩展名的情况。CommonJS模块规范也允许在标识符中不包含文件扩展名,这种情况下,Node会先查找是否存在没有后缀的该文件,如果没有,再按.js、.json、.node的次序补足扩展名,依次尝试
在尝试的过程中,需要调用fs模块同步阻塞式地判断文件是否存在。因为Node是单线程的,所以这里是一个会引起性能问题的地方。小诀窍是:如果是.node和.json文件,在传递给require()的标识符中带上扩展名,会加快一点速度。另一个诀窍是:同步配合缓存,可以大幅度缓解Node单线程中阻塞式调用的缺陷。
目录分析和包。
在分析标识符的过程中,require()通过分析文件扩展名之后,可能没有查找到对应文件,但却得到一个目录,这在引入自定义模块和逐个模块路径进行查找时经常会出现,此时Node会将目录当做一个包来处理
在这个过程中,Node对CommonJS包规范进行了一定程度的支持。首先,Node在当前目录下查找package.json(CommonJS包规范定义的包描述文件),通过JSON.parse()解析出包描述对象,从中取出main属性指定的文件名进行定位。如果文件名缺少扩展名,将会进入扩展名分析的步骤。
如何在一个模块中访问另外一个模块中定义的变量呢global是最容易想到的方法,把一个模块定义的变量复制到全局环境global中,然后另一个模块访问全局环境即可。
这种方法虽然简单,但由于会污染全局环境,不推荐使用。
而常用的方法是使用nodejs提供的模块对象Module,该对象保存了当前模块相关的一些信息。如图:
module.exports属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports变量。
为了方便,Node为每个模块提供一个exports变量,指向module.exports。造成的结果是,在对外输出模块接口时,可以向exports对象添加方法
console.log(module.exports === exports) //true
