Web1.0时代,JavaScript脚本语言的两个主要功能:前端
Web2.0时代,前端工程师利用JavaScript大大提高了网页的用户体验,经历了工具类库、组件库、前端框架、前端应用的变迁。node
JavaScript的先天缺陷:模块。express
高级语言的模块化机制:npm
commonJS的愿景:但愿JavaScript可以在任何地方运行。json
JavaScript规范的缺陷:后端
CommonJS规范中,CommonJSAPI能够编写的应用:数组
模块引用浏览器
采用require()方法;缓存
var math = require('math');
模块定义安全
require():用来引入外部模块;
exports:导出模块的方法或变量,惟一导出的出口;
module:表明模块自身;
// math.js exports.add = function (){ var sum = 0, i = 0, args = arguments, l = args.lenght; while(i<l){ sum += args[i++]; } return sum; } //program.js var math = require("math"); exports.increment = function (val){ return math.add(val,1); }
模块标识:
就是传递给require()方法的参数,符合小驼峰命名的字符串,或者以.、..开头的相对路径或绝对路径,能够没有后缀.js。
Node中引入模块经历的三个步骤:
Node中,模块分为两种:
核心模块部分在Node源代码的编译过程当中,编译了二进制执行文件。在Node进程启动时,部分核心模块被直接加载进内存中,因此这部分核心模块引入时,文件定位和编译执行两个步骤能够省略掉,而且在路径分析中优先判断,它的加载速度是最快的。
文件模块在运行时动态加载,须要完整的路径分析、文件定位、编译执行过程,速度比核心模块慢。
与前端浏览器会缓存静态脚本文件以提升性能同样,Node对引入过的模块都会进行缓存,以减小二次引入的开销。不一样的是,浏览器仅缓存文件,而Node缓存的是编译和执行后的对象。
不管是核心模块仍是文件模块,require()方法对相同模块的二次加载都一概采用缓存优先的方式,这是第一优先级的。不一样之处在于核心模块的缓存检查先于文件模块的缓存检查。
模块标识符分析
模块标识符在Node中的分类:
核心模块
核心模块的优先级仅次于缓存加载,在Node的源代码编译过程当中,已经编译为二进制代码了,其加载过程最快。
路径形式的文件模块
在分析路径模块时,require()将路径转化为真实路径,以真实路径做为索引,将编译执行后的结果放在缓存中,以使二次加载更快,其加载速度慢于核心模块。
自定义模块
这类模块的查找最费时,也是全部方式最慢的一种。
模块路径:
Node在定位文件模块的具体文件时制定的查找策略,具体表现为一个路径组成的数组。
从缓存加载的优化策略使得二次引入时不须要路径分析、文件定位和编译执行的过程,大大提升了再次加载模块时的效率。
文件定位过程当中,须要注意的细节,包括文件扩展名的分析、目录和包的处理。
文件扩展名分析
require()在分析标识符的过程当中,出现标识符中不包含文件扩展名的状况,Node会按.js、.json、.node的次序补足扩展名,依次尝试。
在尝试的过程当中,须要调用fs模块同步阻塞式低判断文件是否存在。
目录分析和包
在分析标识的过程当中,require()经过分析文件扩展名以后,可能没有查找到对应的文件,但却获得一个目录,此时Node会将目录当作一个包来处理。
在Node中,每一个文件模块都是一个对象。
编译和执行时引入文件模块的最后一个阶段。定位到具体的文件后,Node会新建一个模块对象,而后根据路径载入并编译。对于不一样的文件扩展名,载入方法也有所不一样。
核心模块分为两部分:
JavaScript核心模块的编译过程:
在此过程当中,JavaScript代码已字符串的形式存储在Node的命名空间中,是不可直接执行的。在启动Node进程时,JavaScript代码直接加载进内存中。在加载的过程当中,JavaScript核心模块经历标识符分析后,直接定位到内存中,比普通的文件模块从磁盘中一处一处查找要快得多。
在引入JavaScript核心模块的过程当中,也经历了头尾包装的过程,而后才执行和导出exports对象。与文件模块区别的地方在于:获取源码的方式(核心模块从内存中加载)以及缓存执行结果的位置。
C/C++模块主内完成核心,JavaScript主外实现封装的模式是Node可以提升性能的常见方式。
内建模块的组织形式;
内建模块的优点:
在Node的全部模块类型中,存在一种依赖层级关系:
文件模块可能依赖核心模块,核心模块可能依赖内建模块。
核心模块的引入流程经历了C/C++层面的内建模块的定义,(JavaScript)核心模块的定义和引入以及(JavaScript)文件模块层面的引入。
核心模块被编译进二进制文件须要遵循必定规则。做为Node的使用者,几乎没有机会参与核心模块的开发。
JavaScript的一个典型的弱点就是位运算。
在JavaScript应用中,会频繁出现位运算的需求,包括转换、编码等过程,经过JavaScript实现,CPU资源会耗费不少。
普通的扩展模块与内建模块的区别在于无须将源代码编译进Node,而是经过dlopen()方式动态加载。
经过GYP工具实现。
require()方法经过解析标识符、路径分析、文件定位,而后加载执行便可。
C/C++扩展模块与JavaScript模块的区别在于加载以后不须要编译,子类执行以后就能够被外部调用了,其加载速度比JavaScript模块速度略快。
使用C/C++扩展模块的一个好处在于能够更加灵活和动态地加载它们,保持Node模块自身简单性的同时,给予Node五=无限的可能性。
C/C++内建模块:属于最底层的模块,属于核心模块,主要经过API给JavaScript核心模块和第三方JavaScript文件模块的调用。
JavaScript核心模块的两个职责:
文件模块:一般由第三方编写,包括普通JavaScript模块的C/C++扩展模块,主要调用方向为普通JavaScript模块调用扩展模块。
包和NPM是将模块联系起来的一种机制。
包组织模块示意图:
CommonJS包的定义:
由包结构和包描述文件两个部分组成,前者用于组织包中的各类文件,后者用于描述包的相关信息,以供外部读取分析。
包实际是一个存档文件,即一个目录直接打包为.zip和tar.gz格式的文件,安装后解压还原为目录。
包目录包含的文件:
包描述文件用于表达非代码相关的信息,是个JSON格式的文件-package.json,位于包的根目录下,是包的重要组成部分。
对于Node而言,NPM帮助完成了第三方模块的发布、安装和依赖等。借助Node与第三方模块之间造成了很好的一个生态系统。
查看帮助;
查看当前NPM版本:
$ npm -v
查看帮助:
$ npm
安装依赖包
安装依赖包是NPM最多见的用法,执行语句:
$ npm install express
1.全局模式安装
$ npm install express -g
2.从本地安装
本地安装只需为NPM指明package.json文件的所在的位置便可。它能够是一个包含package.json的存档文件,也能够是个URL地址,也能够是个目录有package.json的目录的位置。
3.从非官方源安装
从非官方安装,能够经过镜像源安装。
$ npm config set underscore --registry=http://registry.url
镜像源安装指定默认源:
$ npm config set registry http://registry.url
C/C++模块其实是编译后才能使用的。package.json中script字段的提出就是让包在安装或者卸载等过程当中提供钩子机制。
发布包
初始化包描述文件;
$ npm init
注册包仓库帐号
$ npm adduser
上传包
$ npm publish .
安装包
$ npm install hello_test_jackson --registy=http://npmjs.org
管理包权限
多人进行发布
$ npm owner ls eventproxy
分析包
在使用NPM的过程当中,或许你不能确认当前目录下可否经过require()顺利引入想要的包,执行npm ls分析包。
$ npm ls
为了同时可以享受NPM上众多的包,同时对本身的包进行保密和限制,现有的解决方案就是企业搭建本身的NPM仓库。
企业混合使用官方仓库和局域仓库的示意图:
对于企业内部而言,私有的可重用模块能够打包到局域NPM仓库,这样能够保持更新的中心化,不至于让各个小项目各自维护相同功能的模块,杜绝经过复制粘贴实现代码共享的行为。
NPM的潜在问题:
对于包的使用者而言,包质量和安全问题须要做为是否采纳模块的一个判断条件。
如何评判一个包的安全和质量?
Kwalitee模块的考察点:
先后端JavaScript分别搁置在HTTP的两端,它们扮演的角色并不一样。
浏览器端的JavaScript:
须要经历从同一个服务器端分发到多个客户端执行,瓶颈在于带宽,须要网络加载代码;
浏览器端的JavaScript:
相同的代码须要屡次执行,瓶颈在于CPU和内存等资源,从磁盘中加载代码;
二者的加载的速度不在一个数量级别。
CommonJS为后端JavaScript制定的规范;
AMD为前端JavaScript制定的规范;
AMD规范是CommonJS模块规范的一个延伸,定义以下;
define(id?,dependenceies?,factory)
模块的id和依赖是可选的,
与Node模块类似之处:
factory的内容就是实际代码的内容;
与Node模块不一样之处:
CMD规范由国内的玉伯提出,与AMD规范的主要区别在于定义模块和依赖引入的部分。
AMD须要在声明模块的时候指定全部的依赖,经过形参传递到模块内容;
define(['dep1','dep2'],function (dep1,dep2){ return function (){}; })
为了让同一个模块能够运行在先后端,须要考虑兼容前端也实现了模块规范的环境。为了保持先后端的一致性,类库开发者须要将类库代码包装在一个闭包内。