0. 小工具 npm: node第三方库管理工具,用来获取第三方包、升级、删除或发布编写的包。 nvm: node的多版本管理工具。 supervisor:监视你对代码的改动,重启当前执行的js文件,通常用在网站、web开发以实时修改并观察结果便于调试。 1. 事件 events内置模块,可注册事件监听器、触发指定的事件信号和相应的事件处理。 事实上,不少操做,如读写文件、http服务器、数据库查询等均内部使用events模块的EventEmitter实现的。 2. 事件循环 Node执行过程当中有个事件循环,还有一个事件队列,依次取出每一个事件以及相应的事件回调函数。 Node的事件循环对外不可见,内部底层由libuv库实现支持。 3. 模块和包 模块以把功能拆分、封装,而后组合起来,供其余模块使用,基本上认为一个文件就是一个模块(js文件、JSON、C/C++实现的扩展等,且Node会以.js、.json、.node的次序补足扩展名,依次尝试分析加载)。 Node的模块和包的实现机制,大多数是参照CommonJS的标准实现的。 模块和包的区别,能够认为包是一个更大的模块功能的集合体(一个目录,在模块基础上的更高层次抽象,提供外部的一些相对固定的接口的函数库),主要用于发布、维护、更新等, 而对使用者来讲,模块和包的区别是透明的,所以常常不做区分。若是package.json或main字段不存在,会尝试寻找index.js做为包的接口。 屡次require同一个模块,不会屡次重复加载该模块,第一次加载时已缓存在内存,后面加载同一个模块时直接从内存缓存读取,不管内置模块或文件模块均会缓存。 4. 调试 调试方式:console.log等或者观察-> FireBug、Chrome 开发者工具 -> V8支持的调试。 Node的命令行调试:node debug xxx.js,可开启调试,进入debug调试后,可经过其余的调试参数进行调试。 V8提供的远程调试:基于TCP协议。 远程:node --debug-brk xxx.js或 node --debug debug.js, 开启调试服务器,可指定端口(=port)参数。 本地:node debug ip:port ip为远程IP地址,port为远程端口,默认为5858,若指定了端口,则用远程指定的端口链接。 PS: 命令行的调试采用的在本地执行了一个远程和本地的两条命令实现的,即node --debug xxx.js + node debug 127.0.0.1:5858 = node debug xxx.js。 此外还能够经过某些IDE可方便的提供调试操做。 (新版本Node,建议使用node inspect替代) 5. 全局对象 node中全局对象:global对象,而浏览器通常为windows对象;一样的,除了全局对象外,其余的全局变量可认为均是全局对象的属性。 通常状况下:在最外层定义的变量(node中没法定义最外层变量)、全局对象的属性、隐式定义的变量(没有使用var声明,直接赋值的)均为全局变量。 注意:尽可能不要使用全局变量,尤为是没有用var声明定义的变量,形成命名空间污染,可能引入BUG。 process:一个全局变量,global对象的属性。它用于描述当前Node.js进程状态的对象,提供了一个与操做系统的简单接口。 console: 控制台输出对象,console.log,console.error,console.warn,console.info,console.trace打印调用堆栈。 6. 经常使用的工具内置模块util util.inherits(Sub, Base),提供从Base类中继承原型链,而不是继承Base类(属性和方法)的工具函数(Sub仅继承Base的原型链)(建议使用此方式实现继承,避免多重继承或深层次的继承树)。 util.inspect(object,[showHidden],[depth],[colors]),是一个将任意对象转换为字符串的方法,一般用于调试和错误输出(不是经过调用对象的toString方法)。 此外还有不少如:util.isArray,util.isDate,util.isRegExp,util.isError等类型测试工具,util.format格式化等工具。 7. 最重要的内置模块之一events事件模块 该模块不只用于用用户代码与Node下层事件循环交互,并且也是其余几乎全部模块的依赖实现的基础,以支持事件响应的核心模块均是events.EventEmitter的子类。 events.EventEmitter对象:事件发射器,封装了事件发射、若干事件监听功能的封装。 EventEmitter.on(event, listener)为指定事件注册一个监听器,接受一个字符串event和一个回调函数listener。 EventEmitter.emit(event, [arg1], [arg2], [...])发射event事件,传递若干可选参数到事件监听器的参数列表。 EventEmitter.once(event, listener)为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后马上解除该监听器。 EventEmitter.removeListener(event, listener)移除指定事件的某个监听器,listener必须是该事件已经注册过的监听器。 EventEmitter.removeAllListeners([event])移除全部事件的全部监听器,若是指定event,则移除指定事件的全部监听器。 EventEmitter对象提供一特殊事件error,遇到异常时发射error事件,当error被发射时EventEmitter 规定若是没有响应的监听器, Node.js会把它看成异常,退出程序并打印调用栈。可为error事件设定监听器,以免被抛出异常未被捕获而致使进程退出。 8. 文件系统内置模块fs fs模块提供文件操做的封装,文件读取、写入、改名、删除、目录遍历、连接等posix文件系统操做,且提供了同步和异步两个版本的操做接口。 如:fs.readFile(filename,[encoding],[callback(err,data)]),若指定了encoding编码,则data值为转化后的字符串,不然为Buffer对象实例的二进制数据。 当读取文件出现错误时,err将会是Error对象实例。 同步版本:fs.readFileSync(filename, [encoding]),读取到的文件内容会以函数返回值的形式返回。若是有错误发生,fs将会抛出异常, 应使用try和catch捕捉并处理异常(与同步I/O函数不一样,node的不少异步操做接口是没有返回值的)。 其余的如:fs.open,fs.read,fs.close,fs.readFile,fs.writeFile,fs.fsync等。 9. HTTP服务器与客户端 http内置模块,比较底层,其封装了一个高效的HTTP服务器和一个简易的HTTP客户端。事实上http的目标不是仅仅做web服务器,还能够作其余的, 如可做为网站、社交应用、代理服务器等,如手动实现HTPP服务器则须要实现更多的操做,故而通常使用封装好了的框架,以提供高级接口和提升开发效率, 如egg,mean、Express、koa等。 http.Server是一个基于事件的HTTP服务器,它的核心由Node.js下层C++部分实现,而接口由 JavaScript 封装,兼顾了高性能与简易性。 http.request是一个HTTP客户端工具,用于向HTTP服务器发起请求。 一样的http.Server继承于EventEmitter的,基于事件,其中有几个比较重要的事件。
request事件:当客户端请求到来时,该事件被触发,提供两个参数req和res,分别是http.ServerRequest和http.ServerResponse的实例,表示请求和响应信息。 connection事件:当TCP链接创建时,该事件被触发,提供一个参数socket,为net.Socket的实例。connection事件的粒度要大于request事件, 由于客户端在Keep-Alive模式下可能会在同一个链接内发送屡次请求。 close事件:当服务器关闭时,该事件被触发。注意不是在用户链接断开时。 此外还有checkContinue、upgrade、clientError事件。其中request事件可在CreateServer时指定,也能够在建立server后,调用on("request", listener)也可。 http.ServerRequest是HTTP请求的信息,是后端开发者最关注的内容;通常在request事件的回调函数中。 http.ServerRequest提供了几个事件用于控制请求体传输。如data、end、close事件,此外还有其余的属性可获取到更多的信息。 获取GET请求内容(URL中?后面的内容):Node.js的url模块中的parse函数提供解析url的功能,可从解析内容对象中的query属性来获取到Get请求数据。 获取POST请求内容:GET请求把全部的内容编码到访问路径中(URL),POST请求的内容所有都在请求体中,Node.js默认是不会解析请求体的, 当须要的时候,需手动来获取请求内容,如经过querystring.parse模块解析获取的数据内容以获取到真正的POST请求格式。 http.ServerResponse是返回给客户端的信息,决定了用户最终能看到的结果。它也是由http.Server的request事件回调做为第二个参数传递的。 此外其有三个函数: response.writeHead:向请求的客户端发送响应头,一次请求最多一次,若没有调用则会默认生成响应头给客户端。 response.write:向请求的客户端发送响应内容,若内容为字符串,则需指定编码方式,若为Buffer对象则不须要;在调用response.end前可屡次调用。 response.end:结束响应,告知客户端全部发送已经完成。当全部要返回的内容发送完毕的时候,该函数必须被调用一次。 它接受两个可选参数,意义和response.write相同。若是不调用该函数,客户端将永远处于等待状态。 http模块提供了两个函数http.request和http.get,功能是做为客户端向HTTP服务器发起请求。 http.request(options, callback):options为关联数组,可指定屡次参数内容,callback回调参数,参数内容为http.ClientResponse的实例。 此外该函数http.request返回一个http.ClientRequest的实例,可向服务端发送数据内容,write以及end函数可调用。 http.get(options, callback) http 模块还提供了一个更加简便的方法用于处理GET请求:http.get。 它是http.request的简化版,惟一的区别在于http.get自动将请求方法设为了GET请求,同时不须要手动调用req.end();一样的返回一个http.ClientRequest的实例。 http.ClientRequest实例对象,有response响应事件,也提供了write和end函数,用于向服务器发送请求体,一般用于POST、PUT等操做。 http.ClientResponse实例对象,也提供了三个事件data、end和close,分别在数据到达、传输结束和链接结束时触发,此外还提供了setEncoding、pause、resume方法, 分别为指定响应data事件的数据解码方式、暂停接收数据和发送事件、从暂停的状态中恢复。 10. 模块加载 node加载模块经过require加载,可加载内置核心模块和外部文件(文件夹)模块(依次以js/json/node文件),内置核心模块具备最高优先权,当外部模块和内置模块命名冲突时,其将加载核心模块。 文件模块的加载有两种方式,一种是按路径加载模块,一种是查找当前路径下node_modules文件夹下对应命名的模块。 若是require参数不以“ / ”、“ ./ ”或“ ../ ”开头,而该模块又不是核心模块,那么就要经过查找node_modules加载模块,此时如require('express')即可代替require('./node_modules/express')。 此时在当前目录下的 node_modules 目录中来查找是否是有这样一个模块。若是没有找到,则会在当前目录的上一层中的 node_modules 目录中继续查找,反复执行这一过程,直到遇到根目录为止。 之因此采起这个查找流程,主要是由于依赖模块可能依赖于其余被依赖的模块,便于查找公共依赖模块,以上溯查找到。 Node.js模块不会被重复加载,这是由于Node.js经过文件名缓存全部加载过的文件模块,因此之后再访问到时就不会从新加载了。 require(some_module) 时的加载顺序: 1. 若some_module是一个核心模块,直接加载,结束。 2. 若some_module以“ / ”、“ ./ ”或“ ../ ”开头,按路径加载some_module,结束。 3. 若当前目录为current_dir,按路径加载current_dir/node_modules/some_module。若是加载成功,结束,若是加载失败,令current_dir为其父目录,回溯,重复这一过程, 直到遇到根目录,抛出异常,结束。 对于不一样的文件扩展名,加载处理方式不一样,js文件则是fs模块加载同步读取文件后编译执行,node文件(C/C++扩展)则用process.dlopen等加载扩展, json文件则是fs模块加载同步读取文件后用JSON.parse解析返回结果,其他文件扩展名的则按照js文件的处理方式同样。 JSON文件在用做项目的配置文件时比较有用,经过require引入便可而不须要单独用fs模块来读取,此外还有模块缓存的方便。 内置核心模块分为:C/C++实现的模块位于Node项目源码的src目录,JavaScript实现的模块则放置于lib目录下。 11. 控制流 Node的异步操做的事件式编程容易将程序的逻辑变得混乱。 异步机制是由事件和回调函数实现的,其容易形成回调循环陷阱(主要是js的做用域对象使用方式错误),以及深层的回调函数嵌套,难以弄清回调函数之间的关系。 有一些手段来下降回调的复杂度、耦合度以实现可读的代码,如:async控制流解耦模块、streamlinejs和jscex以同步的方式编写代码,而在执行时异步实现、 或者eventproxy,其实现了对事件发射器的深度封装,采用一种彻底基于事件松散耦合的方式来实现控制流的梳理。不过这些方法也带来了一些复杂度。 12. Node不适合的场景 1. CPU计算密集型的任务。Node单线程基于事件,CPU占用过多则会影响其余请求响应,对于计算密集型任务可交给其余服务器的其余进程或专门进程处理,而后返还给服务器 并在适时发给客户端。 2. 单用户多任务型应用。Node单线程,须要利用多核资源时只能经过多进程的方式通讯处理任务,而多进程相互协做时比较麻烦。 3. 逻辑十分复杂的事务。Node基于事件、异步回调机制,其实现方式并非线性的,对于复杂的逻辑处理会比较困难,难以实现和维护, Node.js更善于处理那些逻辑简单但访问频繁的任务。 4. Node.js不支持完整的Unicode,不少字符没法用string表示。这不是Node.js的缺陷,而是JavaScript标准的问题,在使用中不少时候把全部的字符看成二进制的Buffer 数据来处理。对于中文支持,则须要:1. 保证JS文件是以UTF-8格式保存。2. JS文件中的writeHead方法中加入内容类型"charset=utf-8"编码。