天天看点

Node.js

简单的说 Node.js 就是运行在服务端的 JavaScript。

Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。

Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。

。。。。。。

Node.js教程适用人群

对于不会运用Python、PHP以及Java等动态编程语言的前端程序员来说,选择Node.js作为一个创建自己的服务的工具是非常明智的。

Node.js是运行在服务端的JavaScript,因此,熟悉Javascript的使用将有助于学习Node.js。

同时,学习该Node.js教程也可以帮助后端程序员部署一些高性能的服务。

安装环境自己去配置,选择自己需要的就可以了, node.js下载地址:http://www.nodejs.org/en/download/

我们先了解下Node.js应用是由哪几部分组成的:

引入required模块:我们可以使用require指令来载入Node.js模块。

http:/ /www.iis7.com/b/wzjk/

创建服务器:服务器可以监听客户端的请求,类似于Apache 、Nginx等HTTP服务器。

接收请求与响应请求:服务器很容易创建,客户端可以使用浏览器或终端发送HTTP请求,服务器接收请求后返回响应数据。

Node.js NPM(网络性能监控器,node包管理器) 使用介绍

NPM是随同Node.js一起安装的包管理工具,能解决Node.js代码部署上的很多问题,常见的使用场景有以下几种:

允许用户从NPM服务器下载别人编写的第三方包到本地使用。

允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。

允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

版本号::

使用NPM下载和发布代码时都会接触到版本号。NPM使用语义版本号来管理代码,这里简单介绍一下。

语义版本号分为X.Y.Z三位,分别代表主版本号、次版本号和补丁版本号。当代码变更时,版本号按以下原则更新。

如果只是修复bug,需要更新Z位。

如果是新增了功能,但是向下兼容,需要更新Y位。

如果有大变动,向下不兼容,需要更新X位。

版本号有了这个保证后,在申明第三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某个范围的版本号。例如"argv": "0.0.x"表示依赖于0.0.x系列的最新版argv。

NPM常用命令::

NPM还提供了很多功能,package.json里也有很多其它有用的字段。

除了可以在npmjs.org/doc/查看官方文档外,这里再介绍一些NPM常用命令。

NPM提供了很多命令,例如install和publish,使用npm help可查看所有命令。

使用npm help <command>可查看某条命令的详细帮助,例如npm help install。

在package.json所在目录下使用npm install . -g可先在本地安装当前命令行程序,可用于发布前的本地测试。

使用npm update <package>可以把当前目录下node_modules子目录里边的对应模块更新至最新版本。

使用npm update <package> -g可以把全局安装的对应命令行程序更新至最新版。

使用npm cache clear可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人。

使用npm unpublish <package>@<version>可以撤销发布自己发布过的某个版本代码。

::::::::::::::::::::::::::::::::::::::::::::::::::::::

Node.js REPL(交互式解释器)

Node.js REPL(Read Eval Print Loop:交互式解释器) 表示一个电脑的环境,类似 Window 系统的终端或 Unix/Linux shell,我们可以在终端中输入命令,并接收系统的响应。

REPL 的交互式的编程环境可以实时的验证你所编写的代码,非常适合于验证 Node.js 和 JavaScript 的相关 API。

Node 自带了交互式解释器,可以执行以下任务:

读取 - 读取用户输入,解析输入了Javascript 数据结构并存储在内存中。

执行 - 执行输入的数据结构

打印 - 输出结果

循环 - 循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。

Node 的交互式解释器可以很好的调试 Javascript 代码。

我们可以输入以下命令来启动 Node 的终端:

$ node

这时我们就可以在 > 后输入简单的表达式,并按下回车键来计算结果。 。。。 使用变量 你可以将数据存储在变量中,并在你需要的使用它。 变量声明需要使用 var 关键字,如果没有使用 var 关键字变量会直接打印出来。 使用 var 关键字的变量可以使用 console.log() 来输出变量。 多行表达式 Node REPL 支持输入多行表达式,这就有点类似 JavaScript。接下来让我们来执行一个 do-while 循环: var x = 0 undefined do { ... x++; ... console.log("x: " + x); ... } while ( x < 5 ); x: 1 x: 2 x: 3 x: 4 x: 5 ... 三个点的符号是系统自动生成的,你回车换行后即可。Node 会自动检测是否为连续的表达式。 下划线()变量 你可以使用下划线()获取表达式的运算结果: var x = 10 var y = 20 x + y 30 var sum = _ console.log(sum) REPL 命令 -ctrl + c - 退出当前终端。 -ctrl + c 按下两次 - 退出 Node REPL。 -ctrl + d - 退出 Node REPL. -向上/向下 键 - 查看输入的历史命令 -tab 键 - 列出当前命令 -.help - 列出使用命令 -.break - 退出多行表达式 -.clear - 退出多行表达式 -.save filename - 保存当前的 Node REPL 会话到指定文件 -.load filename - 载入当前 Node REPL 会话的文件内容。 停止 REPL 前面我们已经提到按下两次 ctrl + c 建就能退出 REPL: (^C again to quit) ::::::::::::::::::::::::::::::::::::::: Node.js 回调函数 Node.js 异步编程的直接体现就是回调。 异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。 回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。 例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。 阻塞代码实例 创建一个文件 input.txt ,内容如下: wodema地址:www.wodema.cn 创建 main.js 文件, 代码如下: var fs = require("fs");

var data = fs.readFileSync('input.txt');

console.log(data.toString());

console.log("程序执行结束!");

以上代码执行结果如下:

$ node main.js

我的妈地址:www.wodema.cn

程序执行结束!

非阻塞代码实例

fs.readFile('input.txt', function (err, data) {

if (err) return console.error(err);

});

以上两个实例我们了解了阻塞与非阻塞调用的不同。第一个实例在文件读取完后才执行完程序。 第二个实例我们呢不需要等待文件读取完,这样就可以在读取文件时同时执行接下来的代码,大大提高了程序的性能。

因此,阻塞按是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内。

:::::::::::::::::::::::::::::::::

Node.js 事件循环

Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高。

Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发。

Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。

Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.

事件驱动程序

Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。

当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。

这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)

在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。

整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:

// 引入 events 模块

var events = require('events');

// 创建 eventEmitter 对象

var eventEmitter = new events.EventEmitter();

以下程序绑定事件处理程序:

// 绑定事件及事件的处理程序

eventEmitter.on('eventName', eventHandler);

我们可以通过程序触发事件:

// 触发事件

eventEmitter.emit('eventName');

Node 应用程序是如何工作的?

在 Node 应用程序中,执行异步操作的函数将回调函数作为最后一个参数, 回调函数接收错误对象作为第一个参数。

接下来让我们来重新看下前面的实例,创建一个 input.txt ,文件内容如下:123456789地址:www.123456789.cn

::::::::::::::::::::::::::::::::

Node.js 事件

Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。

Node.js 里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。 你可以通过require("events");来访问该模块。

EventEmitter介绍

events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就 是事件发射与事件监听器功能的封装。

EventEmitter 的每个事件由一个事件名和若干个参 数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。

当事件发射时,注册到这个事件的事件监听器被依次调用,事件参数作 为回调函数参数传递。

EventEmitter常用的API

EventEmitter.on(event, listener)、emitter.addListener(event, listener) 为指定事件注册一个监听器,接收一个字符串 event 和一个回调函数 listener。

server.on('connection', function (stream) {

console.log('someone connected!');

EventEmitter.emit(event, [arg1], [arg2], [...]) 发射 event 事件,传 递若干可选参数到事件监听器的参数表。

EventEmitter.once(event, listener) 为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。

server.once('connection', function (stream) {

console.log('Ah, we have our first user!');

EventEmitter.removeListener(event, listener) 移除指定事件的某个监听 器,listener 必须是该事件已经注册过的监听器。

var callback = function(stream) {

};

server.on('connection', callback);

// ...

server.removeListener('connection', callback);

EventEmitter.removeAllListeners([event]) 移除所有事件的所有监听器, 如果指定 event,则移除指定事件的所有监听器。

error 事件

EventEmitter 定义了一个特殊的事件 error,它包含了"错误"的语义,我们在遇到 异常的时候通常会发射 error 事件。

当 error 被发射时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并打印调用栈。

我们一般要为会发射 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:

var emitter = new events.EventEmitter();

emitter.emit('error');

继承 EventEmitter

大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。

为什么要这样做呢?原因有两点:

首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发射应该是一个对象的方法。

其次JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。

::::::::::::::::::::::::::::::::::::::::

Node.js Buffer(缓冲区)

JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。

但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。

在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。

创建 Buffer 类

Node Buffer 类可以通过多种方式来创建。

方法 1

创建长度为 10 字节的 Buffer 实例:

var buf = new Buffer(10);

方法 2

通过给定的数组创建 Buffer 实例:

var buf = new Buffer([10, 20, 30, 40, 50]);

方法 3

通过一个字符串来创建 Buffer 实例:

var buf = new Buffer("www.123456789.cn", "utf-8");

utf-8 是默认的编码方式,此外它同样支持以下编码:"ascii", "utf8", "utf16le", "ucs2", "base64" 和 "hex"。

写入缓冲区

语法

写入 Node 缓冲区的语法如下所示:

buf.write(string[, offset[, length]][, encoding])

参数

参数描述如下:

-string - 写入缓冲区的字符串。

-offset - 缓冲区开始写入的索引值,默认为 0 。

-length - 写入的字节数,默认为 buffer.length

-encoding - 使用的编码。默认为 'utf8' 。

返回值

返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。

从缓冲区读取数据

读取 Node 缓冲区数据的语法如下所示:

buf.toString([encoding[,start[,end]]])

-start - 指定开始读取的索引位置,默认为 0。

-end - 结束位置,默认为缓冲区的末尾。

解码缓冲区数据并使用指定的编码返回字符串。

将 Buffer 转换为 JSON 对象

将 Node Buffer 转换为 JSON 对象的函数语法格式如右:buf.toJSON()

返回 JSON 对象。

缓冲区合并

Node 缓冲区合并的语法如下所示:

Buffer.concat(list[, totalLength])

-list - 用于合并的 Buffer 对象数组列表。

-totalLength - 指定合并后Buffer对象的总长度。

返回一个多个成员合并的新 Buffer 对象。

缓冲区比较

Node Buffer 比较的函数语法如下所示, 该方法在 Node.js v0.12.2 版本引入:

buf.compare(otherBuffer);

otherBuffer - 与 buf 对象比较的另外一个 Buffer 对象。

返回一个数字,表示 buf 在 otherBuffer 之前,之后或相同。

拷贝缓冲区

Node 缓冲区拷贝语法如下所示:

buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

-targetBuffer - 要拷贝的 Buffer 对象。

-targetStart - 数字, 可选, 默认: 0

-sourceStart - 数字, 可选, 默认: 0

-sourceEnd - 数字, 可选, 默认: buffer.length

没有返回值。

缓冲区裁剪

Node 缓冲区裁剪语法如下所示:

buf.slice([start[, end]])

-start - 数字, 可选, 默认: 0

-end - 数字, 可选, 默认: buffer.length

返回一个新的缓冲区,它和旧缓冲区指向同一块内存,但是从索引 start 到 end 的位置剪切。

缓冲区长度

Node 缓冲区长度计算语法如下所示:

buf.length;

返回 Buffer 对象所占据的内存长度。

:::::::::::::::::::::::::::::::::::::::::::

Node.js Stream(流)

Stream 是 Node.js 中非常重要的一个模块,应用广泛。

Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。

该抽象接口是可读、可写或是既可读又可写的,通过这些接口,我们可以和磁盘文件、套接字、HTTP请求来交互,实现数据从一个地方流动到另一个地方的功能。

Node.js,Stream 有四种流类型:

-Readable - 可读操作。

-Writable - 可写操作。

-Duplex - 可读可写操作.

-Transform - 操作被写入数据,然后读出结果。

所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:

-data - 当有数据可读时触发。

-end - 没有更多的数据可读时触发。

-error - 在接收和写入过程中发生错误时触发。

-finish - 所有数据已被写入到底层系统时触发。

管道流

管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。

链式流

链式是通过连接输出流到另外一个流并创建多个对个流操作链的机制。链式流一般用于管道操作。

接下来我们就是用管道和链式来压缩和解压文件。

::::::::::::::::::::::::::::::::::::::::::

Node.js模块系统

为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。

模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。

创建模块

在 Node.js 中,创建一个模块非常简单,如下我们创建一个 'main.js' 文件,代码如下:

var hello = require('./hello');

hello.world();

以上实例中,代码 require('./hello') 引入了当前目录下的hello.js文件(./ 为当前目录,node.js默认后缀为js)。

Node.js 提供了exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。

服务端的模块放在哪里

也许你已经注意到,我们已经在代码中使用了模块了。像这样:

var http = require("http");

...

http.createServer(...);

Node.js中自带了一个叫做"http"的模块,我们在我们的代码中请求它并把返回值赋给一个本地变量。

这把我们的本地变量变成了一个拥有所有 http 模块所提供的公共方法的对象。

Node.js 的 require方法中的文件查找策略如下:

由于Node.js中存在4类模块(原生模块和3种文件模块),尽管require方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。

从文件模块缓存中加载

尽管原生模块与文件模块的优先级不同,但是都不会优先于从文件模块的缓存中加载已经存在的模块。

从原生模块加载

原生模块的优先级仅次于文件模块缓存的优先级。require方法在解析文件名之后,优先检查模块是否在原生模块列表中。以http模块为例,尽管在目录下存在一个http/http.js/http.node/http.json文件,require("http")都不会从这些文件中加载,而是从原生模块中加载。

原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。

从文件加载

当文件模块缓存中不存在,而且不是原生模块的时候,Node.js会解析require方法传入的参数,并从文件系统中加载实际的文件,加载过程中的包装和编译细节在前一节中已经介绍过,这里我们将详细描述查找文件模块的过程,其中,也有一些细节值得知晓。

require方法接受以下几种参数的传递:

-http、fs、path等,原生模块。

-./mod或../mod,相对路径的文件模块。

-/pathtomodule/mod,绝对路径的文件模块。

-mod,非原生模块的文件模块。

Node.js 函数

在JavaScript中,一个函数可以作为另一个函数接收一个参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。

Node.js中函数的使用与Javascript类似,举例来说,你可以这样做:

function say(word) {

console.log(word);

}

function execute(someFunction, value) {

someFunction(value);

execute(say, "Hello");

以上代码中,我们把 say 函数作为execute函数的第一个变量进行了传递。这里返回的不是 say 的返回值,而是 say 本身!

这样一来, say 就变成了execute 中的本地变量 someFunction ,execute可以通过调用 someFunction() (带括号的形式)来使用 say 函数。

当然,因为 say 有一个变量, execute 在调用 someFunction 时可以传递这样一个变量。

匿名函数

我们可以把一个函数作为变量传递。但是我们不一定要绕这个"先定义,再传递"的圈子,我们可以直接在另一个函数的括号中定义和传递这个函数:

execute(function(word){ console.log(word) }, "Hello");

我们在 execute 接受第一个参数的地方直接定义了我们准备传递给 execute 的函数。

用这种方式,我们甚至不用给这个函数起名字,这也是为什么它被叫做匿名函数 。

函数传递是如何让HTTP服务器工作的

带着这些知识,我们再来看看我们简约而不简单的HTTP服务器:

http.createServer(function(request, response) {

response.writeHead(200, {"Content-Type": "text/plain"});

response.write("Hello World");

response.end();

}).listen(8888);

现在它看上去应该清晰了很多:我们向 createServer 函数传递了一个匿名函数。

用这样的代码也可以达到同样的目的:

function onRequest(request, response) {

http.createServer(onRequest).listen(8888);

::::::::::::::::::::::::::::::::::::::::::::::

Node.js 路由

我们要为路由提供请求的URL和其他需要的GET及POST参数,随后路由需要根据这些数据来执行相应的代码。

因此,我们需要查看HTTP请求,从中提取出请求的URL以及GET/POST参数。这一功能应当属于路由还是服务器(甚至作为一个模块自身的功能)确实值得探讨,但这里暂定其为我们的HTTP服务器的功能。

我们需要的所有数据都会包含在request对象中,该对象作为onRequest()回调函数的第一个参数传递。但是为了解析这些数据,我们需要额外的Node.JS模块,它们分别是url和querystring模块。

当然我们也可以用querystring模块来解析POST请求体中的参数,稍后会有演示。

现在我们来给onRequest()函数加上一些逻辑,用来找出浏览器请求的URL路径:

var url = require("url");

function start() {

var pathname = url.parse(request.url).pathname;

console.log("Request for " + pathname + " received.");

console.log("Server has started.");

exports.start = start;

好了,我们的应用现在可以通过请求的URL路径来区别不同请求了--这使我们得以使用路由(还未完成)来将请求以URL路径为基准映射到处理程序上。

在我们所要构建的应用中,这意味着来自/start和/upload的请求可以使用不同的代码来处理。稍后我们将看到这些内容是如何整合到一起的。

现在我们可以来编写路由了,建立一个名为router.js的文件,添加以下内容:

function route(pathname) {

console.log("About to route a request for " + pathname);

exports.route = route;

如你所见,这段代码什么也没干,不过对于现在来说这是应该的。在添加更多的逻辑以前,我们先来看看如何把路由和服务器整合起来。

我们的服务器应当知道路由的存在并加以有效利用。我们当然可以通过硬编码的方式将这一依赖项绑定到服务器上,但是其它语言的编程经验告诉我们这会是一件非常痛苦的事,因此我们将使用依赖注入的方式较松散地添加路由模块。

:::::::::::::::::::::::::::::::::::::::::

Node.js 全局对象

现在介绍 Node.js 全局对象,global 全局对象无需引用就可以直接使用。

JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。

在浏览器 JavaScript 中,通常window 是全局对象, 而Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性。

我们在Node.js 中能够直接访问到对象通常都是 global 的属性,如 console、process 等,

全局对象与全局变量

global 最根本的作用是作为全局变量的宿主。按照 ECMAScript 的定义,满足以下条件的变量是全局变量:

在最外层定义的变量;

全局对象的属性;

隐式定义的变量(未定义直接赋值的变量)。

当你定义一个全局变量时,这个变量同时也会成为全局对象的属性,反之亦然。需要注 意的是,在Node.js 中你不可能在最外层定义变量,因为所有用户代码都是属于当前模块的, 而模块本身不是最外层上下文。

注意: 永远使用var 定义变量以避免引入全局变量,因为全局变量会污染 命名空间,提高代码的耦合风险。

process

process 是一个全局变量,即 global 对象的属性。

它用于描述当前Node.js 进程状态 的对象,提供了一个与操作系统的简单接口。通常在你写本地命令行程序的时候,少不了要 和它打交道。下面将会介绍process 对象的一些最常用的成员方法。

process.argv是命令行参数数组,第一个元素是 node,第二个元素是脚本文件名, 从第三个元素开始每个元素是一个运行参数。

console.log(process.argv);

将以上代码存储为argv.js,通过以下命令运行:

$ node argv.js 1991 name=byvoid --v "Carbo Kuo"

[ 'node',

'/home/byvoid/argv.js',

'1991',

'name=byvoid',

'--v',

'Carbo Kuo' ]

process.stdout是标准输出流,通常我们使用的 console.log() 向标准输出打印 字符,而 process.stdout.write() 函数提供了更底层的接口。

process.stdin是标准输入流,初始时它是被暂停的,要想从标准输入读取数据, 你必须恢复流,并手动编写流的事件响应函数。

process.stdin.resume();

process.stdin.on('data', function(data) {

process.stdout.write('read from console: ' + data.toString());

process.nextTick(callback)的功能是为事件循环设置一项任务,Node.js 会在 下次事件循环调响应时调用 callback。

初学者很可能不理解这个函数的作用,有什么任务不能在当下执行完,需要交给下次事 件循环响应来做呢?

我们讨论过,Node.js 适合I/O 密集型的应用,而不是计算密集型的应用, 因为一个Node.js 进程只有一个线程,因此在任何时刻都只有一个事件在执行。

如果这个事 件占用大量的CPU 时间,执行事件循环中的下一个事件就需要等待很久,因此Node.js 的一 个编程原则就是尽量缩短每个事件的执行时间。process.nextTick() 提供了一个这样的 工具,可以把复杂的工作拆散,变成一个个较小的事件。

functiondoSomething(args, callback) {

somethingComplicated(args);

callback();

doSomething(functiononEnd() {

compute();

我们假设compute() 和somethingComplicated() 是两个较为耗时的函数,以上 的程序在调用 doSomething() 时会先执行somethingComplicated(),然后立即调用 回调函数,在 onEnd() 中又会执行 compute()。下面用process.nextTick() 改写上 面的程序:

process.nextTick(callback);

改写后的程序会把上面耗时的操作拆分为两个事件,减少每个事件的执行时间,提高事 件响应速度。

注意: 不要使用setTimeout(fn,0)代替process.nextTick(callback), 前者比后者效率要低得多。

我们探讨了process对象常用的几个成员,除此之外process还展示了process.platform、 process.pid、process.execPath、process.memoryUsage() 等方法,以及POSIX 进程信号响应机制。有兴趣的读者可以访问http://nodejs.org/api/process.html 了解详细 内容。

console

console 用于提供控制台标准输出,它是由Internet Explorer 的JScript 引擎提供的调试 工具,后来逐渐成为浏览器的事实标准。

Node.js 沿用了这个标准,提供与习惯行为一致的 console 对象,用于向标准输出流(stdout)或标准错误流(stderr)输出字符。 ? console.log():向标准输出流打印字符并以换行符结束。

console.log 接受若干 个参数,如果只有一个参数,则输出这个参数的字符串形式。如果有多个参数,则 以类似于C 语言 printf() 命令的格式输出。

第一个参数是一个字符串,如果没有 参数,只打印一个换行。

console.error():与console.log() 用法相同,只是向标准错误流输出。

console.trace():向标准错误流输出当前的调用栈。

:::::::::::::::::::::::::::::::::::::

Node.js 常用工具 util

现在介绍Node.js常用工具util。

util作为Node.js的一个核心模块,能够提供常用函数的集合,弥补核心JavaScript的功能过于精简的不足。

util.inherits

util.inherits(constructor, superConstructor)是一个实现对象间原型继承的函数。

与常见的基于类的不同,JavaScript的面向对象特性是基于原型的。JavaScript没有提供对象继承的语言级别特性,而是通过原型复制来实现的。

在这里我们只介绍util.inherits的用法

注意:Sub仅仅继承了Base在原型中定义的函数,而构造函数内部创造的base属性和sayHello函数都没有被Sub继承。

同时,在原型中定义的属性不会被console.log作为对象的属性输出。如果我们去掉objSub.sayHello(); 这行的注释,

util.inspect

util.inspect(object,[showHidden],[depth],[colors])方法可以将任意对象转换为字符串,通常用于调试和错误输出。它至少接受一个object参数,即要转换的对象。

showHidden是一个可选参数,如果值为true,将会输出更多隐藏信息。

depth表示最大递归的层数,如果对象很复杂,你可以指定层数以控制输出信息的多少。如果不指定depth,则默认递归2层,指定为null时表示将不限递归层数完整遍历对象。 如果color值为true,则输出格式将会以ANSI颜色编码,通常用于在终端显示更漂亮的效果。

特别要指出的是,util.inspect并不会简单地直接把对象转换为字符串,即使该对象定义了toString方法也不会调用。

util.isArray(object)

如果给定的参数 "object" 是一个数组返回true,否则返回false。

util.isRegExp(object)

如果给定的参数"object"是一个正则表达式返回true,否则返回false。

util.isDate(object)

如果给定的参数 "object" 是一个日期返回true,否则返回false。

util.isError(object)

如果给定的参数 "object" 是一个错误对象返回true,否则返回false。

::::::::::::::::::::::::::::::::::::::

Node.js 文件系统

Node.js文件系统被封装在fs模块中,它提供了文件的读取、写入、更名、删除、遍历目录、链接等POSIX文件系统操作。

与其他模块不同的是,fs模块中所有的操作都提供了异步的和同步的两个版本,例如读取文件内容的函数有异步的fs.readFile()和同步的fs.readFileSync()。我们以几个函数为代表,介绍fs常用的功能,并列出fs所有函数的定义和功能。

fs.readFile

Node.js读取文件函数语法如下:

-fs.readFile(filename,[encoding],[callback(err,data)])

-filename(必选),表示要读取的文件名。

-encoding(可选),表示文件的字符编码。

-callback 是回调函数,用于接收文件的内容。

如果不指定encoding,则callback就是第二个参数。回调函数提供两个参数err和data,err表示有没有错误发生,data是文件内容。如果指定了encoding,data是一个解析后的字符串,否则data将会是以Buffer形式表示的二进制数据。

fs.readFileSync

fs.readFileSync(filename, [encoding])是fs.readFile同步的版本。它接受和 fs.readFile 相同的参数,而读取到的文件内容会以函数返回值的形式返回。如果有错 误发生,fs将会抛出异常,你需要使用try和catch捕捉并处理异常。

注意:与同步I/O函数不同,Node.js中异步函数大多没有返回值。

fs.open

fs.open(path, flags, [mode], [callback(err, fd)])是POSIX open函数的封装,类似于C语言标准库中的fopen函数。它接受两个必选参数,path为文件的路径, flags 可以是以下值:

r :以读取模式打开文件。

r+ :以读写模式打开文件。

w :以写入模式打开文件,如果文件不存在则创建。

w+ :以读写模式打开文件,如果文件不存在则创建。

a :以追加模式打开文件,如果文件不存在则创建。

a+ :以读取追加模式打开文件,如果文件不存在则创建

fs.read

fs.read语法格式如下:

fs.read(fd, buffer, offset, length, position, [callback(err, bytesRead, buffer)])

参数说明:

-fd: 读取数据并写入buffer指向的缓冲区对象。

-offset: 是buffer的写入偏移量。

-length: 是要从文件中读取的字节数。

-position: 是文件读取的起始位置,如果position的值为null,则会从当前文件指针的位置读取。

-callback:回调函数传递bytesRead和buffer,分别表示读取的字节数和缓冲区对象。

::::::::::::::::::::::::::::::::::::::::::::

Node.js 工具模块

在 Node.js 模块库中有很多好用的模块。这些模块都是很常见的,并同时开发基于任何节点的应用程序频繁使用。接下来我们为大家介绍几种常用模块的使用:

序号 模块名 描述

1 OS 模块 提供基本的系统操作函数。

2 Path 模块 提供了处理和转换文件路的工具。

3 Net 模块 用于底层的网络通信。提供了服务端和客户端的的操作。

4 DNS 模块 用于解析域名。

5 Domain 模块 简化异步代码的异常处理,可以捕捉处理try catch无法捕捉的。

以上就是常用的Node.js工具模块

Node.js Web 模块

现在介绍Node.js Web模块,首先,你应该先了解什么是Web服务器。

什么是 Web 服务器?

Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序。

Web服务器的基本功能就是提供Web信息浏览服务。它只需支持HTTP协议、HTML文档格式及URL,与客户端的网络浏览器配合。

大多数web服务器都支持服务端的脚本语言(php、python、ruby)等,并通过脚本语言从数据库获取数据,将结果返回给客户端浏览器。

目前最主流的三个Web服务器是Apache、Nginx、IIS。

-Client - 客户端,一般指浏览器,浏览器可以通过HTTP协议向服务器请求数据。

-Server - 服务端,一般指Web服务器,可以接收客户端请求,并向客户端发送响应数据。

-Business - 业务层, 通过Web服务器处理应用程序,如与数据库交互,逻辑运算,调用外部程序等。

-Data - 数据层,一般由数据库组成。

使用 Node 创建 Web 服务器

Node.js提供了http模块,http模块主要用于搭建HTTP服务端和客户端,如果要使用HTTP服务器或客户端功能,则必须调用http模块,代码如下:

var http = require('http');

使用 Node 创建 Web 客户端

使用Node创建Web客户端需要引入http模块,创建client.js文件,代码如下所示:

// 用于请求的选项

var options = {

host: 'localhost',

port: '8081',

path: '/index.htm'

// 处理响应的回调函数

var callback = function(response){

// 不断更新数据

var body = '';

response.on('data', function(data) {

body += data;

response.on('end', function() {

// 数据接收完成

console.log(body);

// 向服务端发送请求

var req = http.request(options, callback);

req.end();

::::::::::::::::::::::::::::::::::

Node.js Express 框架

Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。

Express 简介

Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。

使用Express可以快速地搭建一个完整功能的网站。

Express 框架核心特性包括:

-可以设置中间件来响应HTTP请求。

-定义了路由表用于执行不同的HTTP请求动作。

-可以通过向模板传递参数来动态渲染HTML页面。

安装 Express

安装Express并将其保存到依赖列表中:

$ npm install express --save

以上命令会将Express框架安装在当期目录的node_modules目录中, node_modules目录下会自动创建express目录。以下几个重要的模块是需要与express框架一起安装的:

-body-parser - node.js中间件,用于处理JSON, Raw, Text和URL编码的数据。

-cookie-parser - 这就是一个解析Cookie的工具。通过req.cookies可以取到传过来的cookie,并把它们转成对象。

-multer - node.js中间件,用于处理enctype="multipart/form-data"(设置表单的MIME编码)的表单数据。

第一个 Express 框架实例

接下来我们使用Express框架来输出"Hello World"。

以下实例中我们引入了express模块,并在客户端发起请求后,响应"Hello World"字符串。

创建express_demo.js文件

请求和响应

Express应用使用回调函数的参数: request和response对象来处理请求和响应的数据。

app.get('/', function (req, res) {

// --

})

request和response对象的具体介绍:

Request 对象 - request对象表示HTTP请求,包含了请求查询字符串,参数,内容,HTTP头部等属性。常见属性有:

-req.app:当callback为外部文件时,用req.app访问express的实例

-req.baseUrl:获取路由当前安装的URL路径

-req.body / req.cookies:获得「请求主体」/ Cookies

-req.fresh / req.stale:判断请求是否还「新鲜」

-req.hostname / req.ip:获取主机名和IP地址

-req.originalUrl:获取原始请求URL

-req.params:获取路由的parameters

-req.path:获取请求路径

-req.protocol:获取协议类型

-req.query:获取URL的查询参数串

-req.route:获取当前匹配的路由

-req.subdomains:获取子域名

-req.accpets():检查请求的Accept头的请求类型

-req.acceptsCharsets / req.acceptsEncodings / req.acceptsLanguages

-req.get():获取指定的HTTP请求头

-req.is():判断请求头Content-Type的MIME类型

Response 对象 - response对象表示HTTP响应,即在接收到请求时向客户端发送的HTTP响应数据。常见属性有:

-res.app:同req.app一样

-res.append():追加指定HTTP头

-res.set()在res.append()后将重置之前设置的头

-res.cookie(name,value [,option]):设置Cookie

-opition: domain / expires / httpOnly / maxAge / path / secure / signed

-res.clearCookie():清除Cookie

-res.download():传送指定路径的文件

-res.get():返回指定的HTTP头

-res.json():传送JSON响应

-res.jsonp():传送JSONP响应

-res.location():只设置响应的Location HTTP头,不设置状态码或者close response

-res.redirect():设置响应的Location HTTP头,并且设置状态码302

-res.send():传送HTTP响应

-res.sendFile(path [,options] [,fn]):传送指定路径的文件 -会自动根据文件extension设定Content-Type

-res.set():设置HTTP头,传入object可以一次设置多个头

-res.status():设置HTTP状态码

-res.type():设置Content-Type的MIME类型

路由

我们已经了解了HTTP请求的基本应用,而路由决定了由谁(指定脚本)去响应客户端请求。

在HTTP请求中,我们可以通过路由提取出请求的URL以及GET/POST参数。

接下来我们扩展Hello World,添加一些功能来处理更多类型的HTTP请求。

创建express_demo2.js文件

静态文件

Express提供了内置的中间件express.static来设置静态文件如:图片,CSS, JavaScript等。

你可以使用express.static中间件来设置静态文件路径。例如,如果你将图片, CSS, JavaScript文件放在public目录下,你可以这么写:

app.use(express.static('public'));

我们可以到public/images目录下放些图片,如下所示:

node_modules

server.js

public/

public/images

public/images/logo.png

让我们再修改下"Hello Word"应用添加处理静态文件的功能。

GET 方法

在表单中通过GET方法提交两个参数,我们可以使用server.js文件内的process_get路由器来处理输入

POST 方法

在表单中通过POST方法提交两个参数,我们可以使用server.js文件内的process_post路由器来处理输入

文件上传

创建一个用于上传文件的表单,使用POST方法,表单enctype属性设置为multipart/form-data。

Cookie 管理

我们可以使用中间件向Node.js服务器发送cookie信息

:::::::::::::::::::::::::::::::::::

Node.js RESTful API

现在介绍Node.js的RESTful API。

什么是 REST?

REST中文解释为,表述性状态传递(英文:Representational State Transfer,简称REST),是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。

表述性状态转移是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是RESTful。

需要注意的是,REST是设计风格而不是标准。REST通常基于使用HTTP,URI和XML(标准通用标记语言下的一个子集)以及HTML(标准通用标记语言下的一个应用)这些现有的广泛流行的协议和标准。REST通常使用JSON数据格式。

HTTP 方法

以下为REST基本架构的四个方法:

-GET - 用于获取数据。

-PUT - 用于添加数据。

-DELETE - 用于删除数据。

-POST - 用于更新或添加数据。

RESTful Web Services

Webservice是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。

RESTful是基于REST架构的Web Services。

由于轻量级以及通过HTTP直接传输数据的特性,Web服务的RESTful方法已经成为最常见的替代方法。可以使用各种语言(比如,Java程序、Perl、Ruby、Python、PHP和Javascript[包括Ajax])实现客户端。

RESTful Web服务通常可以通过自动客户端或代表用户的应用程序访问。但是,这种服务的简便性让用户能够与之直接交互,使用它们的Web浏览器构建一个GET URL并读取返回的内容。

:::::::::::::::::::::::::::::::::::::::::::::::::::

Node.js 多进程

Node.js本身是以单线程的模式运行的,但它使用的是事件驱动来处理并发,这样有助于我们在多核 cpu 的系统上创建多个子进程,从而提高性能。

每个子进程总是带有三个流对象:child.stdin, child.stdout和child.stderr。他们可能会共享父进程的stdio流,或者也可以是独立的被导流的流对象。

Node提供了child_process模块来创建子进程,方法有:

-exec - child_process.exec使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回。

-spawn - child_process.spawn使用指定的命令行参数创建新进程。

-fork - child_process.fork是spawn()的特殊形式,用于在子进程中运行的模块,如fork('./son.js')相当于spawn('node', ['./son.js']) 。与spawn方法不同的是,fork会在父进程与子进程之间,建立一个通信管道,用于进程之间的通信。

exec() 方法

child_process.exec使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回。

语法如下所示:

child_process.exec(command[, options], callback)

参数说明如下:

-command: 字符串, 将要运行的命令,参数使用空格隔开

-options :对象,可以是:

-cwd,字符串,子进程的当前工作目录

-env,对象,环境变量键值对

-encoding,字符串,字符编码(默认: 'utf8')

-shell,字符串,将要执行命令的Shell(默认: 在UNIX中为/bin/sh, 在Windows中为cmd.exe, Shell应当能识别-c开关在UNIX中,或/s /c在 Windows中。 -在Windows中,命令行解析应当能兼容cmd.exe)

-timeout,数字,超时时间(默认: 0)

-maxBuffer,数字, 在stdout或stderr中允许存在的最大缓冲(二进制),如果超出那么子进程将会被杀死(默认: 200*1024)

-killSignal,字符串,结束信号(默认:'SIGTERM')

-uid,数字,设置用户进程的ID

-gid,数字,设置进程组的ID

-callback :回调函数,包含三个参数error, stdout和stderr。

-exec()方法返回最大的缓冲区,并等待进程结束,一次性返回缓冲区的内容。

spawn() 方法

child_process.spawn使用指定的命令行参数创建新进程,语法格式如下:

child_process.spawn(command[, args][, options])

-command: 将要运行的命令

-args: Array字符串参数数组

-options Object

-cwd:String,子进程的当前工作目录

-env:Object,环境变量键值对

-stdio:Array

String,子进程的stdio配置

-detached:Boolean,这个子进程将会变成进程组的领导

-uid:Number,设置用户进程的ID

-gid:Number,设置进程组的ID

-spawn()方法返回流 (stdout & stderr),在进程返回大量数据时使用。进程开始执行spawn()时就开始接收响应。

fork 方法

child_process.fork是spawn()方法的特殊形式,用于创建进程,语法格式如下:

child_process.fork(modulePath[, args][, options])

-modulePath: String,将要在子进程中运行的模块

-args: Array,字符串参数数组

-options:Object

-execPath:String,创建子进程的可执行文件

-execArgv:Array,子进程的可执行文件的字符串参数数组(默认: process.execArgv)

-silent:Boolean,如果为true,子进程的stdin,stdout和stderr将会被关联至父进程,否则,它们将会从父进程中继承。(默认为:false)

返回的对象除了拥有ChildProcess实例的所有方法,还有一个内建的通信信道。

Node.js JXcore 打包

Node.js是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。

JXcore是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。

JXcore 安装

下载JXcore安装包,然后进行解压,在解压的目录下提供了jx二进制文件命令,接下来我们主要使用这个命令。

步骤1、下载

在http://jxcore.com/downloads/中下载JXcore安装包,你需要根据你自己的系统环境来下载安装包:

1、Window系统下载:Download,

2、Linux/OSX下载安装命令,直接下载解压包下的jx二进制文件,然后拷贝到/usr/bin目录下:

$ wget https://s3.amazonaws.com/nodejx/jx_rh64.zip

$ unzip jx_rh64.zip

$ cp jx_rh64/jx /usr/bin

将/usr/bin添加到PATH路径中:

$ export PATH=$PATH:/usr/bin

以上步骤如果操作正确,使用以下命令,会输出版本号信息:

$ jx --version

v0.10.32

包代码

例如,我们的Node.js项目包含以下几个文件,其中index.js是主文件:

drwxr-xr-x 2 root root 4096 Nov 13 12:42 images

-rwxr-xr-x 1 root root 30457 Mar 6 12:19 index.htm

-rwxr-xr-x 1 root root 30452 Mar 1 12:54 index.js

drwxr-xr-x 23 root root 4096 Jan 15 03:48 node_modules

drwxr-xr-x 2 root root 4096 Mar 21 06:10 scripts

drwxr-xr-x 2 root root 4096 Feb 15 11:56 style

接下来我们使用jx命令打包以上项目,并指定index.js为Node.js项目的主文件:

$ jx package index.js index

以上命令执行成功,会生成以下两个文件:

-index.jxp:这是一个中间件文件,包含了需要编译的完整项目信息。

-index.jx:这是一个完整包信息的二进制文件,可运行在客户端上。

继续阅读