首页 > 代码库 > Node.js【4】简介、安装和配置、快速入门

Node.js【4】简介、安装和配置、快速入门

笔记来自《Node.js开发指南》BYVoid编著

第1章 Node.js简介


Node.js是一个让JavaScript运行在服务端的开发平台,它让JavaScript成为脚本语言世界的一等公民,在服务端堪与PHP、Python、Perl、Ruby平起平坐。


Node.js可以作为服务器向用户提供服务,与PHP、Python、RubyonRails相比,它跳过了Apache、Nginx等HTTP服务器,直接面向前端开发。


Node.js还可以调用C/C++的代码,这样可以充分利用已有的诸多函数库,也可以将对性能要求非常高的部分用C/C++来实现。


Node.js最大的特点就是采用异步式I/O与事件驱动的架构设计。对于高并发的解决方案,传统的架构是多线程模型,也就是为每个业务逻辑提供一个系统线程,通过系统线程切换来弥补同步式I/O调用时的时间开销。Node.js使用的是单线程模型,对于所有I/O都采用异步式的请求方式,避免了频繁的上下文切换。Node.js在执行的过程中会维护一个事件队列,程序在执行时进入事件循环等待下一个事件到来,每个异步式I/O请求完成后会被推送到事件队列,等待程序进程进行处理。

【图】Node.js的架构



第2章 安装和配置Node.js


Node.js开发环境搭建(Windows、Linux&Mac)
Node.js多版本管理器


第3章 Node.js快速入门


$node-e"console.log(‘HelloWorld‘);"
HelloWorld
我们可以把要执行的语句作为node-e的参数直接执行。

REPL(Read-eval-printloop),即输入—求值—输出循环。运行无参数的node将会启动一个JavaScript的交互式shell。如果你执行了一个函数,那么REPL还会在下面显示这个函数的返回值。如果你输入了一个错误的指令,REPL则会立即显示错误并输出调用栈。在任何时候,连续按两次Ctrl+C即可推出Node.js的REPL模式。

【图】Node.js与PHP的架构


Node.js只有在第一次引用到某部份时才会去解析脚本文件,以后都会直接访问内存,避免重复载入。supervisor可以帮助你实现这个功能,它会监视你对代码的改动,并自动重启Node.js。使用方法很简单,首先使用npm安装supervisor:
$npminstall-gsupervisor
使用supervisor命令启动app.js:
$supervisorapp.js

【图】多线程同步式I/O


【图】单线程异步式I/O


单线程事件驱动的异步式I/O比传统的多线程阻塞式I/O究竟好在哪里呢?简而言之,异步式I/O就是少了多线程的开销。对操作系统来说,创建一个线程的代价是十分昂贵的,需要给它分配内存、列入调度,同时在线程切换的时候还要执行内存换页,CPU的缓存被清空,切换回来的时候还要重新从内存中读取信息,破坏了数据的局部性。

3.1 回调函数

【码】readfile.js
// readfile.js

var fs = require('fs');

fs.readFile('file.txt', 'utf-8', function(err, data) {
	if (err) {
		console.error(err);
	} else {
		console.log(data);
	}
});

console.log('end.');
【码】readfilesync.js
// readfilesync.js

var fs = require('fs');

var data = http://www.mamicode.com/fs.readFileSync('file.txt', 'utf-8');>在Node.js中,异步式I/O是通过回调函数来实现的。

【码】readfilecallback.js
// readfilecallback.js

function readFileCallBack(err, data) {
	if (err) {
		console.error(err);
	} else {
		console.log(data);
	}
}

var fs = require('fs');
fs.readFile('file.txt', 'utf-8', readFileCallBack);
console.log('end.');
fs.readFile调用时所做的工作只是将异步式I/O请求发送给了操作系统,然后立即返回并执行后面的语句,执行完以后进入事件循环监听事件。当fs接收到I/O请求完成的事件时,事件循环会主动调用回调函数以完成后续工作。

3.2 事件

Node.js所有的异步I/O操作在完成时都会发送一个事件到事件队列。在开发者看来,事件由EventEmitter对象提供。前面提到的fs.readFile和http.createServer的回调函数都是通过EventEmitter来实现的。

【码】event.js
//event.js

var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();

// 注册事件了事件some_event的一个监听器
event.on('some_event', function() {
	console.log('some_event occured.');
});

setTimeout(function() {
	// 发送事件some_event
	event.emit('some_event');
}, 1000);
Node.js在什么时候会进入事件循环呢?答案是Node.js程序由事件循环开始,到事件循环结束,所有的逻辑都是事件的回调函数,所以Node.js始终在事件循环中,程序入口就是事件循环第一个事件的回调函数。

【图】事件循环


3.3 模块和包

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

在前面的例子中,我们曾经用到了varhttp=require(‘http‘),其中http是Node.js的一个核心模块,其内部是用C++实现的,外部用JavaScript封装。我们通过require函数获取了这个模块,然后才能使用其中的对象。

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

3.3.1 单次加载

require不会重复加载模块,也就是说无论调用多少次require,获得的模块都是同一个。

【码】loadmodule.js
var hello1 = require('./module');
hello1.setName('ichenxiaodoa');
hello1.sayHello();

var hello2 = require('./module');
hello2.setName('ichenxiaodao2');

hello1.sayHello();
hello2.sayHello();
/**
 * 因为hello1和hello2指向的是同一个实例,
 * 所以hello2.setName覆盖hello1.setName,
 * 最终输出 Hello ichenxiaodao2
 */

3.3.2 覆盖exports

有时候我们只是想把一个对象封装到模块中。

【码】singleobject.js
function Hello() {
	var name;

	this.setName = function(thyName) {
		name = thyName;
	};

	this.sayHello = function() {
		console.log('Hello ' + name);
	};
};

exports.Hello = Hello;
此时我们在其他文件中需要通过require(‘./singleobject‘).Hello来获取Hello对象,这略显冗余,可以用下面方法稍微简化:
【码】hello.js
function Hello() {
	var name;

	this.setName = function(thyName) {
		name = thyName;
	};

	this.sayHello = function() {
		console.log('Hello ' + name);
	};
};

module.exports = Hello;
这样就可以直接获得这个对象了:
【码】gethello.js
var Hello = require('./hello');

var hello = new Hello();
hello.sayHello();
hello.setName('ichenxiaodao');
hello.sayHello();
注意,模块接口的唯一变化是使用module.exports=Hello代替了exports.Hello=Hello。在外部引用该模块时,其接口对象就是要输出的Hello对象本身,而不是原先的exports。

事实上,exports本身仅仅是一个普通的空对象,即{},它专门用来声明接口,本质上是通过它为模块闭包的内部建立了一个有限的访问接口。因为它没有任何特殊的地方,所以可以用其他东西来代替,譬如我们上面例子中的Hello对象。

3.3.3 创建包

包是在模块基础上更深一步的抽象,Node.js的包类似于C/C++的函数库或者Java/.Net的类库。它将某个独立的功能封装起来,用于发布、更新、依赖管理和版本控制。Node.js根据CommonJS规范实现了包机制,开发了npm来解决包的发布和获取需求。

Node.js的包是一个目录,其中包含一个JSON格式的包说明文件package.json。严格符合CommonJS规范的包应该具备以下特征:
? package.json必须在包的顶层目录下;
? 二进制文件应该在bin目录下;
? JavaScript代码应该在lib目录下;
? 文档应该在doc目录下;
? 单元测试应该在test目录下。

Node.js对包的要求并没有这么严格,只要顶层目录下有package.json,并符合一些规范即可。当然为了提高兼容性,我们还是建议你在制作包的时候,严格遵守CommonJS规范。

【码】somepackage/index.js
exports.hello=function(){
	console.log('Hello');
}

【码】getpackage.js

varsomepackage=require('./somepackage');
somepackage.hello();

在前面例子中的somepackage文件夹下,我们创建一个叫做package.json的文件,内容如下所示:

{
"main":"./lib/interface.js"
}

然后将index.js重命名为interface.js并放入lib子文件夹下。以同样的方式再次调用这个包,依然可以正常使用。


Node.js在调用某个包时,会首先检查包中package.json文件的main字段,将其作为包的接口模块,如果package.json或main字段不存在,会尝试寻找index.js或index.node作为包的接口。

package.json是CommonJS规定的用来描述包的文件。详细的设置看这里。

3.3.4 Node.js包管理器

本地安装:npm[install/i][package_name]
全局安装:npm[install/i]-g[package_name]

当我们要把某个包作为工程运行时的一部分时,通过本地模式获取,如果要在命令行下使用,则使用全局模式安装。

把全局包链接到本地:
在工程目录下运行:npmlink[package_name]

把本地包链接到全局:
在包目录中运行:npmlink

3.3.5 发布包

1、通过npminit交互式回答产生一个符合标准的package.json。
2、创建一个index.js作为包的借口。
3、使用npmadduser根据提示输入用户名、密码、邮箱,等待账号创建完成。
4、完成后可以使用npmwhoami测验是否已经取得了账号。
5、在package.json所在目录下运行npmpublish,稍等片刻就可以完成发布了。

如果该包日后有更新,只需要在package.json文件中修改version字段,然后重新使用npmpublish命令就行了。

如果你对已发布的包不满意,可以使用npmunpublish命令来取消发布。

3.3.6 调试

命令行调试
nodedebug[script.js]

远程调试
node--debug[=port]script.js
node--debug-brk[=port]script.js

使用Eclipse调试Node.js
安装插件:ChromeDeveloper

使用node-inspector调试
1、安装node-inspector:npminstall-gnode-inspector
2、启动node-inspector
3、启动脚本:node--debug-brk=5858debug.js
4、在浏览器中打开http://127.0.0.1:8080/debug?port=5858

node-inspector使用了WebKitWebInspector,因此只能在Chrome、Safari等WebKit内核的浏览器中使用,而不支持Firefox或InternetExplorer。


文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
  • 原文网址:http://blog.csdn.net/cdztop/article/details/33130969
  • 最后修改时间:2014年06月22日 03:24