1.2 开始Node开发
Node学习指南
现在你已经安装了Node,是时候开始编写第一个Node应用程序了。
1.2.1 Hello, World in Node
为了测试新的开发环境、语言或者工具,第一个写出来的程序往往是“Hello,World”。我们同样也将使用Node创建一个“Hello,World”程序,它仅仅简单的向访问它的用户输出问候语。
示例1-1包含了使用Node创建Hello,World程序需要的全部文本代码。
示例1-1 Node版Hello, World
代码被保存在名为helloworld.js的文件中。作为服务端开发使用的Node,其代码既不冗长,也不模糊。即使是一个不曾接触过Node的人,也可以直观地看出代码所表达的意思。不过可能最吸引人的是,它采用了我们很熟悉的JavaScript语言编写程序。
可以在Linux系统中使用命令行,或在Mac OS中使用终端窗口,或在Windows中使用命令窗口,运行如下命令来启动示例程序:
node helloworld.js
在程序成功运行后,会输出如下信息:
Server running at 8124
现在,你应该可以使用任何浏览器访问站点了。如果应用程序是在本地机器上运行的,可以使用localhost:8124。如果是在远程机器上运行的,需要使用远程机器的URL并访问8124端口。在浏览器中,一个显示有“Hello,World!”内容的页面将会被显示出来。到目前为止,你已经成功创建了第一个完整的并且可以正常工作的Node应用程序了。
警告:
如果是在Fedora系统中安装Node环境,需要留意Node会被重命名,以避免与系统中现有功能的冲突。更多详细信息请查阅http://nodejs.tchol.org/。
由于我们没有在node命令后使用&(使应用程序在后台运行),在程序启动后,你就不能返回到命令行了。不过你依然可以继续正常访问应用程序,并且同样的信息会被显示在浏览器窗口中,直到你使用Ctrl+C来停止程序,或使用kill命令来中止node进程。
如果想在后台运行应用程序,在Linux系统中可以使用如下命令:
node helloworld.js &
之后,你需要通过“ps–ef”命令找到进程对应的ID,然后使用kill命令手动关闭该进程(比如进程ID为3747):
ps -ef | grep node
kill 3747
如果你退出终端窗口,node进程同样也会中止。
提示:
在第16章中,我会讨论如何创建一个可持久的Node应用程序安装。
你不能启动另外一个监听同一端口的Node应用程序:因为在同一时间、同一端口上,只能运行一个Node应用程序。如果你的Apache工作在端口80上,你也不能在该端口上启动Node应用程序。你必须为每一个应用使用不同的端口号。
如果你在使用WebMatrix的话,也可以将helloworld.js作为一个新文件添加到之前生成的WebMatrix站点项目中。你只需要打开站点,从菜单中选择“New File…”选项,然后将示例1-1中的代码输入到创建的文件中,点击运行按钮。
警告:
WebMatrix会覆盖Node程序中使用的端口号。当你运行应用程序后,你只能通过项目中定义好的端口访问站点,而不能使用在http.Server.listen方法中指定的端口。
1.2.2 分析“Hello,World”
我会在后续章节对Node应用程序进行更多剖析。但是现在,我们先来仔细看看“Hello, World”程序。
在示例1-1中,第一行代码是:
var http = require('http');
Node中的许多功能通过外部程序或库来提供,我们叫它模块(modules)。这句代码其实就是用来加载HTTP模块,然后指派给一个本地变量。HTTP模块能提供基本的HTTP功能,可以让应用程序支持对网络的访问。
下一句代码是:
http.createServer(function (req, res) { ...
在这行代码中,使用了createServer方法创建了一个新的服务器,并且传递了一个匿名函数来作为该方法时的参数。这个匿名函数就是requestListener函数,它有两个参数:一个代表服务器收到的请求(http.ServerRequest),另一个代表服务器的响应(http.ServerResponse)。
在匿名函数中,有如下代码:
res.writeHead(200, {'content-Type': 'text/plain'});
在http.ServerResponse对象中有一个writeHead方法,我们用它来发送响应信息的HTTP头,并且指定了HTTP状态码(status code)为200,同时还提供了内容类型content-type。你同样可以通过headers对象来设置其他HTTP响应头中需要的信息,例如content-length或者connection:
{ 'content-length': '123',
'content-type': 'text/plain',
'connection': 'keep-alive',
'accept': '*/*' }
writeHead的第二个可选参数是reasonPhrase,用来对状态码指定文本描述。
依据下面代码将“Hello,World!”内容放入响应信息中,并发送响应:
res.end("Hello, World!\n");
调用http.ServerResponse.end方法表示本次通信已经完成,所有响应信息的头和内容均已经被发送。注意:你必须为每一个http.SErverResponse对象使用该方法。
end方法有两个参数:
一个数据块,可以是一个字符串或者buffer对象;
如果数据块是字符串对象,第二个参数用于指定编码方式。
这两个参数都是可选的,而且只有在字符串是非utf8编码的情况下才需要指定第二个参数,因为其默认值是utf8。
我们也可以不在end方法中传递数据块,而使用另一个write方法:
res.write("Hello, World!\n");
然后:
res.end();
下面一句代码,表示了匿名函数和createServer函数的结束:
}).listen(8124);
http.Server.listen方法紧接在createServer之后调用,用于在指定端口(本例中为8124)监听接入的客户端连接。它的可选参数是一个hostname和一个回调函数。如果指定了hostname,客户端将能通过Web地址的形式访问服务端了,比如http://oreilly.com或者http://examples.burningbird.net。
提示:
本章后半部分对callback函数有更多介绍。
listen方法是异步的,这意味着在应用程序等待客户端连接建立时,不会阻塞程序的执行。listen方法之后的所有代码都会被执行。而且当连接建立起来后,一个listening事件会被触发,传给listen方法的回调函数会被执行。
最后一句代码是:
console.log('Server running on 8124/');
console是一个起源于浏览器环境并被Node采用的众多对象之一,大多数JavaScript开发人员对它都很熟悉。在此处,它提供了将文本信息输出到命令行(或者开发环境)的功能,而不再是输出信息到客户端浏览器中。