Felix的Node.js初学者入门指南

本文为译文,翻译自原文 http://nodeguide.com/beginner.html

网上有很多关于node.js的资料,但鉴于其快速的开发进度,初学者可能会很难找到比较好且最新的入门资料。本文就是在力求提供这份资料,并且力求时刻和最新的node.js稳定版本保持同步。

本guide已经针对node 0.4.x的最新改动做过更新,node 0.4.x也是当前node的稳定分支。

学习JavaScript

本guide假设你已经对JavaScript有所熟悉。如果你不是很熟悉,你可以去读读:Marijn Haverbeke写的Eloquent JavaScript,它可以在网上免费阅读。

Hello World Tutoriali

本tutorial会指导你如何安装node.js,其中包括构建一个简单的所谓“hello world”的http服务器。

安装

首先:当前为了使用node.js,你还得有一台运行着*nix操作系统的电脑。虽然推荐使用Linux和OSX,但想在FreeBSD或cygwin(windows上)上跑node.js应该也不是什么不可能的事情。目前,一个完整的windows版本的nodejs移植正在如火如荼的进行之中,只是还尚未到可以公开给public使用的程度。

常见的安装node.js的方法是从源代码直接编译。不同的包管理系统,催生出目前所存在的很多种不同的安装包,由于它们各自的更新频率又各有不同,我还是推荐从源代码安装。

你可以在nodejs.org获得最新的源代码。使用下面的命令可以下载并安装v0.4.4版本的node.js。

$ wget http://nodejs.org/dist/node-v0.4.4.tar.gz
$ tar -xzf node-v0.4.4.tar.gz
$ cd node-v0.4.4.tar.gz
$ ./configure
$ sudo make install

Node.js本身除了依赖一些常用的构建工具(依赖python是因为构建系统本身需要)并没有别的更多的外部依赖。在OSX上,需要先安装Xcode才能确保安装成功,在Ubuntu上你则可能需要运行:

$ apt-get -y install build-essential

交互式的node.js shell

如果一切正常,你应该可以像下面这样开始调用交互式的node.js shell了:

$ node
> console.log('Hello World');
Hello World

这个交互式的shell(也被称为REPL)不仅是个可以测试简单如一行的代码的好地方,也可以被直接嵌入到你的node.js应用程序里面。如果想退出,只要按下Ctrl + C即可。

这个REPL还自带了很多非常好的功能,比如最重要的tab补全功能。

你的第一个程序

简单的说,编写一个node.js程序就是创建一个以’.js’扩展名结尾的文件。比如你可以用下面的内容创建一个简单的’hello_world.js’文件:

console.log('Hello World');

保存文件后,你可以像下面这样在终端里面执行它:

$ node hello.js
Hello World

“hello world” http服务器

只在终端里面打印hello world并没有那么振奋人心,让我们更进一步来编写一个通过http协议来返回’hello world’的程序。我们把这个程序命名为’hello_http.js’,这个文件包含以下的内容:

var http = require('http');

var server = http.createServer(function(req, res) {
  res.writeHead(200);
  res.end('Hello Http');
});
server.listen(8080);

现在让我们来运行它,在终端里面敲入:

$ node hello_http.js

首先注意到的是,不同于我们的第一个程序,这个程序不会马上退出。这是因为node程序会一直运行,直到它确认不再会有更多的事件发生后才会退出。就我们这个case来讲,那个一直开放着的http服务器就可以源源不断的产生事件,以保持程序继续运行。

测试这个服务器非常简单,只要打开一个新的浏览器tab,访问:http://localhost:8080/ 即可。事遂人愿,你应该可以在浏览器页面上看到那个包含字符串’Hello Http’的服务器应答。

或者,你也可以尝试在终端里面使用curl来测试你的服务器:

$ curl localhost:8080
Hello Http

下面让我们来深入看看我们的这个小程序里面所包含的各个步骤。在第一行,我们include了http核心模块,并把它赋值给了一个叫做http的变量。在后面的section,我们还会提到更多有关模块系统的信息。

然后我们定义了一个变量server,并把http.createServer的返回值赋给它。传给http.createServer的参数是个闭包,每当有新的http请求过来的时候,这个闭包都会被调用。

最后我们调用server.listen(8080),以告诉node.js我们想让我们的服务器监听在哪个端口。如果想监听80端口,你的程序需要以root权限来运行。

现在你在浏览器里面访问’localhost:8080’,那个connection闭包就会被调用,调用的同时系统还会传入一个req和一个res对象。这个req对象是个可读的流,每当有部分数据从客服端过来的时候,它便会产生一个’data’事件(就像表单提交或者文件上传)。res对象则是个可写的流,被用来向客户端发回数据。在上面那个例子里,我们只是简单的返回了200 OK的header以及’Hello Http’的body内容。

模块系统

node.js提供了一个简单的模块系统,以便于我们组织程序,把各部分功能分开放到不同的文件里面。

让我们创建一个叫’main.js’的文件以示说明,文件内容如下:

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

你可能已经猜到,那句require(‘./hello’)便是用来加载其它JavaScript文件的内容的。开头的’./’表示这个将要被加载的文件就在’main.js’所在的目录下面。另外值得注意的是,我们并不需要提供文件扩展名,因为’.js’就是默认的后缀。

接下来让我们来创建这个将被加载的’hello.js’文件,内容如下:

exports.world = function() {
  console.log('Hello World');
}

这儿值得注意的是,我们给一个叫做’exports’的对象赋上了一个叫’world’的属性。这样的一个’exports’对象在各个模块里面都存在,每当调用require函数来include一个模块时,这个’exports’对象就会被当作返回值返回。如果我们现在运行’main.js’,应该会看到意料中的输出:

$ node main.js
Hello World

有一点值得提一下,现在很多的node用户都喜欢像下面这样直接修改覆盖exports对象:

module.exports = function() {
  // ...
}

如你所料,这样的改写会使require调用直接返回上面所赋值的function。这在你使用面向对象编程时变的非常有用,因为每个文件的exports便是那个类的构造函数。

接下了你需要知道的是,如果require调用里面不包含所include文件是相对地址的暗示时,模块系统又是怎么处理的。比如:

var http = require('http');

在这种情况下,node.js会首先查看一下是不是有一个叫作http的核心模块存在,如果存在(http核心模块确实存在)便直接返回。但是如果不存在这样的核心模块呢,比如说’mysql’?

var mysql = require('mysql');

这种情况下,node.js会遍历这个目录树,从里到外依次遍历各个父目录,检查是否有个叫’node_modules’的目录存在。如果找到这样命名的目录,node.js便会在这个目录下查找一个叫’mysql.js’的文件。如果很不幸没有找到,并且已经遍历到达根目录’/’,node.js只能放弃并抛出异常。

当前,除了各级父目录,node.js查找模块文件的时候还会遍历另外一个目录列表里存放的所有目录,这个目录列表可以通过require.paths访问和修改。然而,关于要不要移除这个功能,目前正有着激烈的讨论,所以你可能最好还是忽略它比较好。

最后但也同样重要的是,node.js也支持你创建一个叫’index.js’的文件,以作为一个目录的主include文件。也就是说,如果你调用require(‘./foo’),’foo.js’文件和foo目录下的index.js文件’foo/index.js’都会被检索到,在不是相对路径的include调用里也是如此。

使用EventEmitters

Node.js以一个叫EventEmitter的类实现了观察者模式。每当有对象会发出或产生出着各种不同事件的时候,node.js经常让这些对象背后的类继承EventEmitter。

使用EventEmitter还是相当直观的。你可以通过调用对象的’on()’函数来监听某个特定事件,’on()’函数需要你提供这个事件的名字,以及一个回调的闭包以作为参数。比如:

var data = '';
req
  .on('data', function(chunk) {
    data += chunk;
  })
  .on('end', function() {
    console.log('POST data: %s', data);
  })

如你所见,on()函数会同时返回自己所属的对象的引用,这可以方便你把很多这样的事件监听调用串联起来而不是写多行调用。(和jquery一样,译者注……)

如果你只是想在第一次事件发生时有所动作,你还可以使用once()函数。

最后,通过调用removeListener函数,你还可以移除某个事件监听者。请注意这个函数的参数是你正想要移除的回调函数的引用,而不是这个事件的名字:

var onData = function(chunk) {
  console.log(chunk);
  req.removeListener(onData);
}

req.on('data', onData);

上面的例子本质上和once()函数是相同的。

下一步

现在你已经知道了node.js的基础,你可能最好开始自己写一些小的程序。node的api文档是个开始的好地方,它可以被用来作为你灵感的源泉。

调试node.js程序

有很多种方法可以用来调试node.js应用程序。我个人崇尚越少的调试越好,所以我严格的遵循测试驱动开发的建议。

不过话说回来,如果在现有已经存在的应用程序里面定位调试某个很tricky的bug的时候,还是有一些方法可以帮到你的。

使用console.log()

理解一个问题最简单粗暴的办法就是使用console.log()来查看这个对象。你可以直接将这个对象作为参数传入:

var foo = {bar: 'foobar'};
console.log(foo);

也可以使用类似sprintf()的的格式化方法来格式化debug输出:

var foo = {bar: 'foobar'};
console.log('Hello %s, this is my object: %j', 'World', foo);

使用node的debugger

如果你不是很喜欢console.log()(觉得它太简单粗暴,译者注……译者怎么这么烦……),或者你觉得你的问题使用断点调试会更有助于你分析时,node自带的debugger是个很好的选择。调用这个debugger只需要:

$ node.js debug my_file.js

还在编辑中,请稍后再来……(广告很短,请不要离开……)

使用WebKit Inspector

还在编辑中,请稍后再来……(这就是译文啊,大哥,我容易么我……)

框架

如果你只是刚刚接触node.js,到了需要解析POST请求,处理url路由或者页面渲染的时候你可能并不想再重造轮子。如果是这样的话,你可能会考虑使用一些比较流行的web框架。这个section会对这些流行的选择做一个简单的概述,并且附上我的看法。

Express

当前,express可能是很多node.js开发人员的不二选择。它相对成熟,而且包括了connect(联想一下rack)中间件。这个框架自带了url路由,配置选项,模板引擎,POST请求解析和其它很多功能。

虽说express是个可靠的框架,但相对于Rails,CakePHP或者Django来说,它目前的规模还是相对较小。从这点来看它更接近于Sinatra,不走运的是,它似乎并没有花很大的力气来以区别于它的Ruby前辈们所做的事情,所以在JavaScript里面使用的时候还是让人感觉不是十分自然。当然不管怎么,目前除了重造轮子编写你自己的框架以外,它绝对是个很好的选择。

fab.js

你觉得你懂JavaScript?再想想。最初受到jQuery的串式调用的启发,fabs.js走了一条非常不同寻常的路(美特斯邦威啊。。译者注),它对JavaScript所做的各做扭曲改动甚至超出了很多人可以理解的范围。每个函数会返回另外一个函数,完全免去了方法或者函数名称的必要性,这种做法的产物便是各种感官上都很像lisp的代码。

目前我并不认为fab.js完全适用于生产环境,当然如果你还在探索node.js的世界中,你绝对应该尝试哪怕一次。不说别的,fab.js至少展示了在web框架领域,JavaScript不一定非要复制Ruby,Python和PHP,它完全可以有它自己独特的路线。(那就是美特斯邦威……译者那啥……)

主机托管和部署

快速简单部署

如果你只是刚开始编写node.js应用,并且你想尽快让它娘的跑起来,可以这样做:

1. 将你的程序copy到要准备部署的服务器。如果你使用git,这可能仅仅就是从另外一台服务器或者像GitHub这样的服务那里clone一个repository。

2. 假设你的项目中包含一个叫’server.js’的文件,进入包含这个文件的目录,并输入:

$ screen
$ node server.js

这会在一个所谓的screen会话中启动你的’server.js’程序。Screen可以让你的shell拥有保持当前状态的功能,即便你退出了那个你用来登陆服务器的终端,这种状态仍旧会被保持。

所以你可以安全的关闭你的终端程序,而你的’server.js’则会继续运行。如果想继续监控其运行状态,你可以重新登陆服务器,输入:

$ screen -r

这会重新让你回到那个一直在背后运行你的程序的shell终端上。

当然,这个方法只被推荐用来进行试验性质的部署。如果你的node应用在某个时刻突然崩溃,screen是不会试图去重启它的,所以不要试图用这个方法在生产环境上部署应用。

Joyent no.de

还在编辑中,请稍后再来……(幸亏人家还在编辑中,不然偶要挂掉了……)

[Footnotes]
  1. 这个我就不翻了吧。。。 []

历史上的今天:

Related posts:

2 Comments on "Felix的Node.js初学者入门指南"

  1. cooldoger United States Google Chrome Windows says:

    nice

Trackbacks for this post

  1. Felix’s Node.js Style Guide (Part 1) | Samson's Weblog United States WordPress Unknow Os

Got something to say? Go for it!

使用新浪微博登陆