自从Javascript诞生以来,从来没有人把它作为一种编程语言来使用。在Web 1.0时代,这种脚本语言主要用于表单验证和网页特效。直到Web 2.0时代,前端工程师利用它大大提升了用户在网页上的体验,JS得到了广泛的重视。随着JS的逐渐普及,经历了工具类库、组件库、前端框架、前端应用的变化。Javascript本来就缺少一个功能:模块,CommonJS规范的出现弥补了这个缺陷。本文将介绍CommonJS规范和节点的模块机制。
在其他高级语言中,Java有类文件,Python有导入机制,PHP有include和require。然而,JS通过脚本标签引入代码的方式是杂乱无章的。过去,人们不得不使用名称空间来人为地限制代码。在CommonJS规范出现之前,前端和后端的Javascript是可以统一的。Node借鉴CommonJS的Modules规范,实现了一个非常易用的模块系统。
1.CommonJS模块规范
CommonJS的模块规范分为三个部分:
1).模块引用:通过require()方法将模块的API引入当前上下文,并传入模块标识符,如var math=require(' math ');2).模块定义:通过exports对象导出当前模块的方法或变量。模块中还有一个模块对象,导出实际上是模块的一个属性。在Node中,文件就是一个模块,模块中的“全局变量”对外界是不可见的。只有装载在导出上的属性是打开的,例如export . add=function(){ };出口。PI=3.14159263).模块ID:其实是传递给require()的参数,比如上面提到的‘math’,必须是符合camel命名法的字符串,或者是以“.”开头的相对路径或者绝对路径".",并且它可以没有文件名后缀”。js "
2.节点模块的实现过程
在Node中,模块分为两类:一类是Node自己提供的核心模块,另一类是用户自己编写的文件模块。部分核心模块在Node源代码编译过程中编译成二进制文件,在Node启动时直接加载到内存中,因此其加载速度最快。文件模块在运行时动态加载,需要经过三个步骤:路径分析、文件定位、编译和执行。注意Node缓存了所有已经引入的模块,以减少二次引入的开销,并采用先从缓存加载同一个模块进行二次加载的策略。
2.1路径分析
路径分析主要分析上述模块标识符,主要分为以下几类:
1)、核心模块,如http、fs、path等。2)、相对路径文件模块以.3)、以/4)开头的绝对路径文件模块和自定义文件模块,它们可以是文件或包的形式。Node会根据模块路径array module.paths一个一个的尝试查找目标文件,通常会沿着当前目录一步一步的找到名为node_modules的目录,一直到根目录,所以这是最耗时的查找方式。
2.2文件定位
在路径分析的基础上,文件定位需要注意以下细节:
1)文件扩展名分析:由于CommonJS规范允许模块id不填写扩展名,Node将按照的顺序尝试。js,json和。节点。2)目录分析和包:如果文件扩展名分析后没有找到对应的文件,但得到了一个目录,则节点会将该目录视为一个包。
2.3编译和执行
定位特定文件后,Node会新建一个模块对象,根据路径加载并编译。不同扩展的加载方法不同:
1).js文件:通过fs模块同步读取文件,编译执行;2).节点文件:这是用C/C写的扩展文件,用dlopen()方法加载;3).json文件:通过fs模块同步读取文件,用JSON.parse()解析,返回结果;4)其他扩展名文件加载为。js文件。
我们知道默认情况下每个模块文件中有三个变量:require、exports和module。甚至在Node的API文档中,我们知道每个模块中有两个变量:文件名和目录名。他们来自哪里?Node的模块如何实现声明的“全局变量”不会实际污染其他模块?事实上,Node会在编译JS模块的过程中包装文件的内容。以下是头尾打包后的JS文件示例:
复制代码如下:(函数(导出,要求,模块,_ _ filename,_ _ dirname) {/*中间是JS文件的实际内容*/var math=require(' math ');exports.area=函数(半径){ return Math。PI *半径*半径;};/* js文件的实际内容以*/})结尾;
这样,每个模块文件的范围被隔离,诸如require、exports和module等变量被注入到模块的上下文中。这是Node对CommonJS模块规范的实现。C/C模块和Node核心模块的编译过程比较复杂,不再赘述。
3.模块调用栈
需要明确节点中各模块的调用关系,如下图所示:
C/C内置模块是最底层的模块,属于核心模块。主要为Javascript核心模块和第三方Javascript文件模块提供API调用。实际上,几乎没有触及到这样的模块。Javascript核心模块的主要职责有两个:一个是作为C/C内置模块的封装层和桥接层供文件模块调用,另一个是纯函数模块,不需要处理底层。文件模块通常由第三方编写,包括普通的Javascript模块和C/C扩展模块。
4.套餐和NPM
4.1包装结构
包本质上是一个归档文件(通常。zip或。tar.gz),它在安装后被解压缩并还原到一个目录中。CommonJS包规范由包结构和包描述文件组成。完全符合CommonJS规范的包结构应该包含以下文件:
1).package.json:包描述文件2)。bin:存储可执行二进制文件的目录3)。lib:用于存储Javascript代码的目录4)。文档:存储文档的目录。测试:存储单元测试用例的目录
4.2包描述文件
包描述文件是一个JSON文件——package.json,位于包的根目录下,是包的重要组成部分,用来描述包的一般信息。后面提到的NPM的所有行为都与这个文件的领域密切相关。下面将以知名的Web framework express项目的package.json文件为例,说明一些常用字段的含义。
1).名称:包名2)。描述:包介绍3)。版本:版本号,以“语义版本控制”为准。参考http://semver.org/4)。依赖项:使用当前包需要依赖的包列表。该属性非常重要,NPM将通过该属性自动加载依赖包。5).存储库:托管源代码的位置列表
其他字段的用法可以参考npmpackage.json的描述。
4.3 NPM公共职能
NPM(节点包管理器)通常被称为节点包管理器。其主要功能是管理节点包,包括安装、卸载、更新、查看、搜索、发布等。
NPM包安装
节点包的安装有两种:本地安装和全局安装。它们之间的区别如下:
1).本地安装的NPM installpackage-name: package将被下载到当前目录,并且只能在当前目录中使用。2).全局安装NPM install-g package-name:package将被下载到特定的系统目录中,并且安装的软件包可以在所有目录中使用。
NPM包管理
以下是grunt-cli(grunt命令行工具)的示例,其中列出了常用的包管理命令:
1).npm install:安装由package.json文件2的dependencies和devDependencies字段声明的所有包)。npm install [emailprotected]:安装特定版本的grunt-cli3)。NPM安装grunt-contrib-copy-save:安装grunt-contrib-copy并保存依赖包. json文件4)。npm卸载grunt-cli:卸载软件包5)。npm列表:检查安装了哪些软件包6)。npm发布文件夹:发布包