异步编程
如果你了解异步(Asynchronous)编程的话,将会非常简单。 这里的A和Ajax里的”A"是同一个单词。在node.js中每一个方法都是异步的。因此事实上任何一个“阻断”线程的东西都运行在后台。这是大家需要务必了解的。例如,如果你尝试阅读一个文件,你需要指定一个Callback方法来控制阅读操作完成后的动作。
你需要自己处理全部
Node.js提供的是一个环境,这个环境中你需要自己处理所有事情。 所以呢,这里没有缺省的web服务器,或者类似服务器。对于新手来说可能有点恐怖,但是回报也是巨大的,你将得到超棒性能的web应用。一个脚本来处理所有客户端的通信。这很大程度的减少了应用消耗的资源。
交互式shell
-
$ node
-
> console.log('Hello World');
-
Hello World
-
undefined
模块
node.js使用了模块化的架构体系来简化复杂应用。模块类似于C的类库和pascal中的unit。每一个模块包含了相关主题的一套功能。例如,你可以使用http模块来处理HTTP相关的功能。同时node.js提供了一些核心的类库帮助你访问文件系统中的文件,创建HTTP和TCP/UDP服务器,及其其它相关的实用功能。包含一个模块非常简单:
var http = require('http');
require()方法返回指定模块的参考。在这个代码中,一个HTTP模块的参考将保存在http变量中。
在以上代码中,我们传递了模块的名字到require()方法。这是的Node搜索node_module目录来查询http模块,如果没有找到模块,将会到全局模块缓存中查询。你可以指定一个实际的文件,如下:
var myModule = require('./myModule.js');
模块包含了代码片段。模块中的代码大部分是私有的 ,这意味你不能在外部直接访问变量。只能在模块内部访问。当然,你可以使用expose来将变量或者方法倒出模块,这样你可以在模块外部使用,如下:
-
var PI = Math.PI;
-
-
exports.area = function (r) {
-
return PI * r * r;
-
};
-
-
exports.circumference = function (r) {
-
return 2 * PI * r;
-
};
这个代码中,PI是一个私有的模块变量。下面两个方法将给予exports对象创建。这些方法能够在模块外部被访问因为它们基于exports方法。因此,你可以保证area()和circumference()将会按照它们原本的行为来实现操作。
全局变量
node.js基于google的v8 javascript引擎,所以我们需要遵守客户端的最佳实践。例如,我们需要避免将任何内容知道全局变量中。当然,不是任何情况都可以做到的。Node中使用的全局范围是GLOBAL,你可以通过忽略var关键字来定义一个全局变量:
安装新模块
Node.js拥有一个包管理器,叫做Node Package Manager(NPM)。自动随node.js安装,你可以使用NPM来安装模块。执行如下语句:
npm install module_name
上面这个命令会把模块安装到当前项目(目录)下,如果要全局安装的话要加 -g 参数。
如果你的程序包含很多“依赖”(Dependency),那再利用该方法安装它们就不合适了。为此npm提供了一个package.json文件。
-
{
-
"name" : "MyStaticServer",
-
"version" : "0.0.1",
-
"dependencies" : {
-
"express" : "3.3.x"
-
}
-
}
package.json文件包含了应用程序的基本信息。其中“dependencies”部分描述了你想安装模块的名称和版本。该案例,接受Express 3.3的任何版本。你可以在该部分列出你想要的所有依赖。
代替之前一个个安装每个依赖,现在我们可以运行一个命令,即可将它们全部安装完成。
$ npm install
运行该命令,npm将在当下文件夹中查找“package.json”文件。一旦找到,即可安装所列出的所有依赖。
如果你是需要一个公司代理才能访问网络的话,就需要进行一些特殊的配置才能让npm可以访问到网络上的资源,以下三种方法任选其一
-
npm --https-proxy=http://proxy.company.com:8080 -g install karma
-
-
or
-
-
npm config set proxy http://proxy.company.com:8080
-
npm config set https-proxy http://proxy.company.com:8080
-
-
or
-
-
export npm_config_proxy http://proxy.company.com:8080
-
export npm_config_https_proxy http://proxy.company.com:8080
文件的输入与输出
通过内置的文件(fs)模块,我们可以很容易进行文件的输入/输出操作。fs模块有一个readFile方法,该方法以文件路径、回调函数为参数。该回调函数在完成文件读取后调用。文件数据读取后存储在Buffer类型中,为基本的字节数组。我们可以通过toString()方法将它转化为字符串。
fs.readFile()方法接受3个参数:文件名称,编码(可选),callback方法。
fs.writeFile()方法接受文件名称和数据作为参数。同时接受第三个和第四个参数(都是可选的)来指定编码和callback方法。
-
// Load the fs (filesystem) module
-
var fs = require('fs');
-
-
// Read the contents of the file into memory.
-
fs.readFile('example_log.txt', function (err, logData) {
-
-
// If an error occurred, throwing it will
-
// display the exception and end our app.
-
if (err) throw err;
-
-
// logData is a Buffer, convert to string.
-
var text = logData.toString();
-
});
异步回调
正如在上例中看到的那样,Node.js典型的模式是使用异步回调。基本上,你告诉Node.js要做的事,它执行完后便会调用你的函数(回调函数)。这是因为Node是单线程的。在你等待回调函数执行过程中,Node可继续执行其他事务,不必被阻塞直到该请求完毕。
这对于Web服务器尤其重要。在现代Web应用访问数据库的过程中特别普遍。当你等待数据库返回结果的过程中,Node可以处理更多请求。与每次连接仅处理一个线程相比,它使你以很小的开销来处理成千上万个并行连接。
HTTP服务器的例子
Node内建有一个模块,利用它可以很容易创建基本的HTTP服务器。
你运行该代码,并在浏览器中输入“”,你将看见该文本。
-
// Include http module.
-
var http = require("http");
-
-
// Create the server. Function passed as parameter is called on every request made.
-
// request variable holds all request parameters
-
// response variable allows you to do anything with response sent to the client.
-
http.createServer(function (request, response) {
-
// Attach listener on end event.
-
// This event is called when client sent all data and is waiting for response.
-
request.on("end", function () {
-
// Write headers to the response.
-
// 200 is HTTP status code (this one means success)
-
// Second parameter holds header fields in object
-
// We are sending plain text, so Content-Type should be text/plain
-
response.writeHead(200, {
-
'Content-Type': 'text/html'
-
});
-
// Send data and end response.
-
response.end('Welcome to GBin1.com!');
-
});
-
// Listen on the 8080 port.
-
}).listen(8080);
处理URL参数
我们需要自己处理Node.js中的任何东西,包括处理参数:
-
// Include http module,
-
var http = require("http"),
-
// And url module, which is very helpful in parsing request parameters.
-
url = require("url");
-
-
// Create the server.
-
http.createServer(function (request, response) {
-
// Attach listener on end event.
-
request.on('end', function () {
-
// Parse the request for arguments and store them in _get variable.
-
// This function parses the url from request and returns object representation.
-
var _get = url.parse(request.url, true).query;
-
// Write headers to the response.
-
response.writeHead(200, {
-
'Content-Type': 'text/plain'
-
});
-
// Send data and end response.
-
response.end('Here is your data: ' + _get['data']);
-
});
-
// Listen on the 8080 port.
-
}).listen(8070);
这个代码使用url模块的parse()方法,来将请求的URL转换为一个对象。返回的对象拥有一个query属性,可以用来取得URL参数。保存这段代码为get.js并且执行:
node get.js
打开浏览器并且输入地址/?data=put_gbstring_here。你将看到效果。
更复杂的文件输入输出例子
代码展示如何使用fs.readFile()和fs.writeFile()。每一次服务器收到一个请求,脚本就会自动添加1,然后写回文件。
-
// Include http module// Include http module,
var http = require("http"),
// And mysql module you've just installed.
fs = require("fs");
// Create the http server.
http.createServer(function (request, response) {
// Attach listener on end event.
request.on("end", function () {
// Read the file.
fs.readFile("test.txt", 'utf-8', function (error, data) {
// Write headers.
response.writeHead(200, {
'Content-Type': 'text/plain'
});
// Increment the number obtained from file.
data = parseInt(data) + 1;
// Write incremented number to file.
fs.writeFile('test.txt', data);
// End response with some nice message.
response.end('This page was refreshed ' + data + ' times!');
});
});
// Listen on the 8080 port.
}).listen(8080);
结果好像有bug,这里每次加了2。其实这不是一个错误。每一次你请求这个URL的时候,俩个请求被发送到服务器。第一个请求是浏览器自动请求的 favion.ico。当然第二个请求是我们脚本对应URL()。所以要对请求进行一个判断,下面是修改后的代码。
-
// Include http module,
-
var http = require("http"),
-
// And mysql module you've just installed.
-
fs = require("fs");
-
-
// Create the http server.
-
http.createServer(function (request, response) {
-
// Attach listener on end event.
-
request.on('end', function () {
-
// Check if user requests /
-
if (request.url == '/') {
-
// Read the file.
-
fs.readFile('test.txt', 'utf-8', function (error, data) {
-
// Write headers.
-
response.writeHead(200, {
-
'Content-Type': 'text/plain'
-
});
-
// Increment the number obtained from file.
-
data = parseInt(data) + 1;
-
// Write incremented number to file.
-
fs.writeFile('test.txt', data);
-
// End response with some nice message.
-
response.end('This page was refreshed ' + data + '
-
});
-
} else {
-
// Indicate that requested file was not found.
-
response.writeHead(404);
-
// And end request without sending any data.
-
response.end();
-
}
-
});
-
// Listen on the 8080 port.
-
}).listen(8080);
执行系统命令
-
//
-
var sys = require('sys')
-
var exec = require('child_process').exec;
-
var child;
-
-
// executes `pwd`
-
child = exec("pwd", function (error, stdout, stderr) {
-
sys.print('stdout: ' + stdout);
-
sys.print('stderr: ' + stderr);
-
if (error !== null) {
-
console.log('exec error: ' + error);
-
}
-
});
或者
-
var sys = require('sys')
-
var exec = require('child_process').exec;
-
function puts(error, stdout, stderr) { sys.puts(stdout) }
-
exec("ls -la", puts);
访问Mysql数据库
对于一个正常的服务器端技术,肯定需要有机制来处理数据库操作。为了在node.js中使用数据库,我们需要安装类库,这里我们使用node-mysql。完整的名称是mysql@2.0.0-alpha2(@后面是版本号)。打开你的控制台,导航到你保存脚本的目录,执行如下命令:
npm install mysql@2.0.0-alpha2
这将下载和安装模块,同时创建node_module目录到目前目录中。下面我们看看如何使用代码访问mysql:
-
// Include http module,
-
var http = require('http'),
-
// And mysql module you've just installed.
-
mysql = require("mysql");
-
-
// Create the connection.
-
// Data is default to new mysql installation and should be changed according to your configuration.
-
var connection = mysql.createConnection({
-
user: "root",
-
password: "",
-
database: "db_name"
-
});
-
-
// Create the http server.
-
http.createServer(function (request, response) {
-
// Attach listener on end event.
-
request.on('end', function () {
-
// Query the database.
-
connection.query('SELECT * FROM your_table;', function (error, rows, fields) {
-
response.writeHead(200, {
-
'Content-Type': 'x-application/json'
-
});
-
// Send data as JSON string.
-
// Rows variable holds the result of the query.
-
response.end(JSON.stringify(rows));
-
});
-
});
-
// Listen on the 8080 port.
-
}).listen(8080);
查询数据库非常简单,输入查询语句和callback方法。在一个实际的应用中,你应该检查是否有错误!(一旦有错误,error参数将不会为undefined),并且发送响应代码基于查询成功或者失败,注意我们设定content-type为x-application/json,这是正确的JSON MIME类型。rows参数包含了查询结果,这里我们使用JSON,stringify()方法简单的将rows中的数据转换为JSON结构
保存脚本为mysql.js,执行:
node mysql.js
在浏览器中输入,你可以看到要求你下载JSON格式数据文件。
调试
Node.js 支持命令行下的单步调试。在命令行下执行 node debug debug.js,将会启动调试工具
命令
|
功能
|
run
|
执行脚本,在第一行暂停
|
restart
|
重新执行脚本
|
cont,c
|
继续执行,直到遇到下一个断点
|
next,n
|
单步执行
|
step,s
|
单步执行并进入函数
|
out,o
|
从函数中步出
|
setBreakpoint(),sb()
|
在当前行设置断点
|
setBreakpoint(‘f()’),sb(...)
|
在函数f的第一行设置断点
|
setBreakpoint(‘script.js’,20),sb(...)
|
在script.js的第20行设置断点
|
clearBreakpoint,cb(...)
|
清除所有断点
|
backtrace,bt
|
显示当前的调用栈
|
list(5)
|
显示当前执行到的前后5行代码
|
watch(expr)
|
把表达式expr加入监视列表
|
unwatch(expr)
|
把表达式expr从监视列表移除
|
watchers
|
显示监视列表中所有的表达式和值
|
repl
|
在当前上下文打开即时求值环境
|
kill
|
终止当前执行的脚本
|
scripts
|
显示当前已加载的所有脚本
|
version
|
显示V8的版本
|
另外还有基于
eclipse 和 node-inspector 的调试方式,这里就不一一介绍了。
自动重启服务
如果你有 PHP 开发经验,会习惯在修改 PHP 脚本后直接刷新浏览器以观察结果,而你在开发 Node.js 实现的 HTTP 应用时会发现,无论你修改了代码的哪一部份,都必须终止Node.js 再重新运行才会奏效。这是因为 Node.js 只有在第一次引用到某部份时才会去解析脚本文件,以后都会直接访问内存,避免重复载入,而 PHP 则总是重新读取并解析脚本(如果没有专门的优化配置)。Node.js的这种设计虽然有利于提高性能,却不利于开发调试,因为我们在开发过程中总是希望修改后立即看到效果,而不是每次都要终止进程并重启。
supervisor 可以帮助你实现这个功能,它会监视你对代码的改动,并自动重启 Node.js。
使用方法很简单,首先使用 npm 安装 supervisor:
-
$ npm install -g supervisor
-
-
$ supervisor app.js
-
DEBUG: Running node-supervisor with
-
DEBUG: program 'app.js'
-
DEBUG: --watch '.'
-
DEBUG: --extensions 'node|js'
-
DEBUG: --exec 'node'
-
DEBUG: Starting child process with 'node app.js'
-
DEBUG: Watching directory '/home/byvoid/.' for changes.
-
HTTP server is listening at port 3000.
-
当代码被改动时,运行的脚本会被终止,然后重新启动。在终端中显示的结果如下:
-
DEBUG: crashing child
-
DEBUG: Starting child process with 'node app.js'
-
HTTP server is listening at port 3000.
同时,也可以使用forever
forever start -a /yourcodepath/app.js
一些有用的资料
初学者教程:
英文API:
中文API:
Express:
某应用:
https://github.com/nswbmw/N-blog
阅读(2044) | 评论(0) | 转发(0) |