3.1Nodejs编程
3.1.1 Hello world
打开记事本
console.log('Hello world!');
3.1.2 node.js命令行工具
3.1.3 建立http服务器
node.js最简单的http服务器就这样诞生了,这个程序调用了nodejs提供的http模块,对所用HTTP请求答复同样的内容并监听3000端口。
小技巧--使用supervisor
改动nodejs代码自动重启服务器。
npm install -g supervisor
3.2 异步式I/O和事件式编程
nodejs最大的特点就是异步式I/O(或者非阻塞I/O)与事件紧密结合的编程模式,这种编程模式与传统的同步式I/O线性编程思路有很大的不同,因为控制流很大程度上要靠事件和回调函数来组织,一个逻辑要拆分为若干个单元。
3.2.1 阻塞与线程
什么是阻塞(block)呢?线程在执行中如果遇到磁盘读写或网络通信(统称I/O操作),通常要耗费较长的时间,这时操作系统会剥夺这个线程的CPU控制权,使其暂停执行,同时将资源让给其他的工作线程,这种线程调度方式称为阻塞。当I/O操作完毕时。操作系统将这个线程的阻塞状态删除,恢复其对CPU的控制权。令其继续执行。这种I/O模式就是通常的同步式I/O(Synchronous I/O)或阻塞式I/O(Blocking I/O)。
异步式I/O(Asynchronous I/O)或非阻塞式I/O(Non-blocking I/O)则针对所有的I/O操作不采用阻塞的策略。当线程遇到I/O操作时,不会以阻塞的方式等待I/O操作的完成或数据的返回,而只是将I/O请求发送给操作系统,继续执行下一条语句。当操作系统完成I/O操作时,以事件的形式通知执行I/O操作的线程,线程会在特定时间处理这个事件,为了处理异步I/O,线程必须有事件循环,不断地检查有没有处理的事件,依次予以处理。
阻塞模式下,一个线程处理一项任务,多线程提高系统吞吐量。
非阻塞模式下,一个线程永远在执行计算操作,CPU利用率100%,I/O以事件方式通知。线程不会被I/O阻塞,永远在利用CPU。
优点:单线程事件驱动的异步式I/O比传统的多线程阻塞式I/O好在哪里?
异步式I/O少了多线程的开销,对操作系统来说,创建一个线程的代缴是十分昂贵的,需要给他分配内存,列入调度,同时在线程切换的时候执行内存换页,CPU的缓存被清空,切换回来的时候还要重新从内存中读取信息,破坏了数据的局部性。
缺点:不符合人们一般的程序设计思维,容易让控制流变得晦涩难懂,给编码和调试带来不小的困难。习惯传统编程模式的开发者在刚刚接触到大规模的异步应用时往往会无所适从,有不少解决异步式编程问题的库。
3.2.2 回调函数
nodejs中如何用异步的方式读取一个文件
代码如下:
结果如下:
同步式读取文件
代码如下:
结果如下:
同步式读取文件的方式比较好理解。
异步式读取文件:异步式I/O请求发送给了操作系统,然后立即返回并执行后面的语句,执行完以后进入事件循环监听事件,当fs接收到I/O请求完成的事件时,事件循环会主动调用回调函数以完成后续工作。
3.2.3 事件
3.3 模块和包
一个node.js文件就是一个模块(这个文件可能是javascript代码、json或者编译过的c/c++扩展)
3.3.2
1、创建模块
module.js
//module.js
var name;
exports.setName = function(thyName){
name = thyName;
};
exports.sayHello = function(){
console.log('hello'+ name);
};
在同一目录下创建getmodule.js
//getmodule.js
var myModule = require('./module');
myModule.setName('BYVoid');
module.js通过exports对象把setName和sayHello作为模块的访问接口,在getmodule.js中通过require('./module')加载这个模块,然后就可以直接访问module.js中exports对象的成员函数了。
2.单次加载
无论调用多少次require。获得的模块都是同一个
- 覆盖exports
3.3.3创建包
node.js的包是个目录。
CommonJS规范的包应该具备以下特征:
package.json 必须在包的顶层目录下
二进制文件应该在bin目录下
javascript代码必须在lib目录下
文档应该在doc目录下
单元测试应该在test目录下
1 作为文件i一个叫做somepackage的文件夹,在其中创建index.js
//somepackage/index.js
exports.hello = function(){
console.log('hello.');
};
然后在somepackage之外建立getpackage.js
//getpackage.js
var somePackage = require('./somepackage');
somePackage.hello();
运行 node getpackage.js 控制台将输出结果hello .
我们把文件夹封装成模块,即所谓的包,包通常是一些模块的集合,在模块的基础上提供了更高的抽象,相当于提供了一些固定接口的函数库,通过定制package.json,我们可以创建更复杂、更完善、更符合规范的包用于发布。
2 package.json
在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规定的用来描述包的文件,完全符合规范的package.json文件应该含有以下字段
name:包的名称,必须唯一,由小写英文字母、数字和下划线组成,不能包含空格
description:包的简要说明
version:符合语义化版本识别规范的版本字符串
keywords:关键词数组,通常用于搜索
maintainers:维护者数组,每个元素要包含name、email(可选)、web(可选)字段
contributors:贡献者数组,格式与maintainers相同,包的作者应该是贡献者数组的第一个元素
bugs:提交bug的地址,可以是网址或者电子邮件地址
licenses:许可证数组,每个元素要包含type(许可证的名称)和Url(链接到许可证文本的地址)字段
repositories:仓库托管地址数组,每个元素要包含type(仓库的类型,如git)、url(仓库地址)和path(相对于仓库的路径,可选)字段
dependencies:包的依赖,一个关联数组,由包名称和版本号组成
{
"name": "mypackage",
"description": "Sample package for CommonJS. This package demonstrates the requiredelements of a CommonJS package.",
"version": "0.7.0",
"keywords": ["package","example"],
"maintainers": [{"name": "Bill Smith","email": "[email protected]",}],
"contributors": [{"name": "BYVoid","web": "http://www.byvoid.com/"}],
"bugs": {"mail": "[email protected]","web": "http://www.example.com/bugs"},
"licenses": [{"type": "GPLv2","url": "http://www.example.org/licenses/gpl.html"}],
"repositories": [{"type": "git","url": "http://github.com/BYVoid/mypackage.git"}],
"dependencies": {"webkit": "1.2","ssl": {"gnutls": ["1.0", "2.0"],"openssl": "0.9.8"}}
}
3.3.4 Nodejs包管理
1获取一个包
npm [install/i] [package_name]
例如你要安装express
npm install express
2npm全局模式
npm install -g supervisor:同时在PATH环境变量中注册supervisor
在mac/Linux 全局模式安装需要root权限
3创建全局连接
全局模式安装的包不能通过require使用,但是通过npm link 可以打破这一局限(不支持windows)
npm link express
./node_modules/express > /usr/local/bin/node_modules/express
除了将全局的包连接到本地以外,npm link还可以将本地的包连接到全局,使用方法(在package.json所在目录)中运行npm link,如果我们要开发一个包,利用这种方法可以非常方便地在不同的工程间进行测试。
4包的发布
npm 可以非常方便地发布一个包,比 pip、 gem、 pear 要简单得多。在发布之前,首先需要让我们的包符合 npm 的规范, npm 有一套以 CommonJS 为基础包规范,但与 CommonJS并不完全一致,其主要差别在于必填字段的不同。通过使用 npm init 可以根据交互式问答产生一个符合标准的 package.json,例如创建一个名为 byvoidmodule 的目录,然后在这个目录中运行npm init:
$ npm init
Package name: (byvoidmodule) byvoidmodule
Description: A module for learning perpose.Package
version: (0.0.0) 0.0.1
Project homepage: (none) http://www.byvoid.com/
Project git repository: (none)
Author name: BYVoidAuthor
email: (none) [email protected]
Author url: (none) http://www.byvoid.com/
Main module/entry point: (none)Test command: (none)
What versions of node does it run on? (~0.6.10)
About to write to/home/byvoid/byvoidmodule/package.json
{
"author": "BYVoid (http://www.byvoid.com/)",
"name": "byvoidmodule",
"description": "A module for learning perpose.",
"version": "0.0.1",
"homepage": "http://www.byvoid.com/",
"repository": {
"url": ""
},
"engines":{
"node": "~0.6.12"
},
"dependencies": {},
" devDependencies": {}
}
Is this ok? (yes) yes
这样就在 byvoidmodule 目录中生成一个符合 npm 规范的 package.json 文件。创建一个index.js 作为包的接口,一个简单的包就制作完成了。在发布前,我们还需要获得一个账号用于今后维护自己的包,使用 npm adduser 根据提示输入用户名、密码、邮箱,等待账号创建完成。完成后可以使用 npm whoami 测验是否已经取得了账号。接下来,在 package.json 所在目录下运行 npm publish,稍等片刻就可以完成发布了。打开浏览器,访问 http://search.npmjs.org/ 就可以找到自己刚刚发布的包了。现在我们可以在世界的任意一台计算机上使用 npm install byvoidmodule 命令来安装它。图3-6 是npmjs.org上包的描述页面。
如果你的包将来有更新,只需要在 package.json 文件中修改 version 字段,然后重新使用 npm publish 命令就行了。如果你对已发布的包不满意(比如我们发布的这个毫无意义的包),可以使用 npm unpublish 命令来取消发布。
3.4 调试
写一个简单的代码
在命令行下执行 node debug debug.js,将会启动调试工具:
3.4.2 远程调试
3.4.4 使用node-inspector调试nodejs
大部分基于 Node.js 的应用都是运行在浏览器中的,例如强大的调试工具 node-inspector。node-inspector 是一个完全基于 Node.js 的开源在线调试工具,提供了强大的调试功能和友好的用户界面,它的使用方法十分简便。首先,使用 npm install -g node-inspector 命令安装 node-inspector,然后在终端中通过 node --debug-brk=5858 debug.js 命令连接你要除错的脚本的调试服务器,启动 node-inspector:$ node-inspector在浏览器中打开 http://127.0.0.1:8080/debug?port=5858, 即可显示出优雅的 Web 调试工具