看完《Node.js 实战》,整理总结了觉得比较有价值的内容。
1、require
require是少有的同步i/o操作,请只在模块初始化时候使用require。
2、exports与module.exports的区别
最终程序里导出的都是module.exports。
而exports只是对module.exports的一个全局引用,如exports.myFunc为module.exports.myFunc的简写。
为了不破坏exports对module.exports的引用,不能设置exports。
如果破坏了,修复方式:
module.exports = exports = Currency;
3、模块缓存与猴子补丁:
Node 能把模块作为对象缓存起来。
如果程序中的两个文件引入了相同的模块,第一个文件会把模块返回的数据存到程序的内存中,这样第二个文件就不用再去访问和计算模块的源文件了。
实际上在第二个引入是有机会修改缓存数据的。这种方式称为“猴子补丁”(monkey patching ):让一个模块可以改变另一个模块的行为,开发人员可以不用创建它的新版本。
4、Node两种常用的响应逻辑组织方式
一次性为回调函数,绑定的为事件(继承event emitter事件发射器,emit发射消息)
这里给个event emitter的例子:
扩展文件监视器
|
|
5、减少if/else引起的回调嵌套
有两种方式,可以结合到一起用:
- (1)嵌套间引入中间函数,通过函数调用拆分嵌套
- (2)尽早从函数中返回
6、Node的异步回调惯例
Node大多数内置模块使用回调会带两个参数,一个是err或者er,一个是存放结果。
7、进程退出会等待事件异步完成
Node的事件轮询会跟踪还没有完成的异步逻辑,只要有未完成的异步逻辑,Node进程就不会退出。事件轮询会跟踪所有数据库连接,知道它们关闭,以防止Node退出。
8、在Node中使用闭包保留全局变量示例
示例,用闭包私有化color值:
这里有一个asyncFunction函数,对它的调用被封装到一个匿名函数里,参数为color。
这样你就可以马上执行这个匿名函数,把当前的 color 的值传给它。而color 变成了匿名函数内部的本地变量,当 匿名函数外面的color 值发生变化时,本地版的color 不会受影响。
|
|
9、Node的content-length与chunk
Node默认是chunk方式传输(块编码)。当设置content-length时,会隐含禁用Node的块编码。设置content-length传输,数据更少,提升性能。
注意:
content-length是字节长度,不是字符长度,一般用Buffer.byteLength(body)
10、__dirname
__dirname表示文件所在目录的路径,在开发时,这个目录和当前工作目录(CWD)是同个目录,但是生产环境可能是从另外一个目录运行。
11、中间件设计惯例
中间件一般有三个参数:请求,响应,回调函数next
为了提供可配置能力,中间件遵循一个惯例:用函数返回另一个函数(闭包)
可配置中间件的基本结构:
|
|
使用:
|
|
12、错误处理中间件
错误处理中间件与普通中间三个参数不同,多了第一个参数err,如:
|
|
当Connect遇到错误,它会跳过后续的中间件,只调用错误处理中间件。
在使用connect时,错误处理有三种方式:
- (1)使用Connect 默认的错误处理器;
- (2)自己处理程序错误;
- (3)使用多个错误处理中间件组件。
第三种给个示例:
多个错误处理器的实现:
api挂载在/api上:
|
|
其中errorHandler处理来自api的所有错误,error处理来自app的所有错误。
13、connect中间件
包括:
- (1)cookieParser:为后续中间件提供req.cookies和req.signedCookies,
设置cookie这样用:res.setHeader(’Set-Cookie’,’’) - (2)bodyParser:为后续中间件提供req.body和req.files
- (3)limit:基于给定字节长度限制请求主体的大小。必须用在bodyParser中间件之前,防止攻击。
更灵活的使用:
|
|
- (4)query:为后续中间件提供req.query
- (5)logger:将http请求的信息输出到stdout或日志文件之类的流中
|
|
输出有颜色区分的日志,便于开发调试
两种输出日志频率
immediate,表示一收到请求,就写日志。
buffer,以毫秒为单位,指定缓冲区刷新的时间间隔。
- (6)favicon:响应/favicon.ico http请求。通常放在中间件logger前面,这样它就不会出现在你的日志中了
- (7)methodOverride:可以替不能使用正确请求方法的浏览器仿造req.method,依赖于bodyParser
- (8)vhost:根据制定的主机名(如nodejs.org)使用给定的中间件和http服务器实例
可以做反向代理,缺点:一个网站崩溃,他的所有网站都会崩溃,一个都在同一个进程 - (9)session:为用户设置一个http回话,并提供一个可以跨域请求的持久化req.session对象,依赖于cookieParser
- (10)basicAuth:为程序提供http基础认证
- (11)csrf:防止http表单中的跨站请求仿造攻击,依赖于session
- (12)errorHandler:当出现错误时把堆栈跟踪信息返回给客户端。在开发时使用,不要在生产环境中使用
- (13)static:把制定目录中的文件发给http客户端,跟connect的挂在功能配合得很好
返回./public目录下的静态资源文件:
|
|
默认请求/js/test.js,会去.public/js/test.js去查找。
使用带挂载的static
|
|
- (14)compress:用gzip压缩优化http响应
- (15)directory:为http客户端提供目录清单服务,基于客户端的accept请求(普通文件,son或html)提供经过优化的结果
14、 Express中两种渲染视图方式
- (1)在程序中使用app.render()
- (2)在请求或者响应层用res.render()
14.1 视图的查找设置
|
|
14.2 设置模板引擎
|
|
14.3 视图缓存
默认会开启view cache,模板修改,需要重启生效。
14.4 视图查找
如photos为复数,暗示是一个资源列表。
15、单元测试与验收测试
有两种形态:测试驱动(TDD)和行为驱动开发(BDD)
- 单元测试有Node的assert,Mocha,node unit,Vows以及should.js框架
- 验收测试,Tobi和Soda框架。
15.1 nodeunit:
例子:创建一个目录,每个测试脚本都应该用测试组装exports对象,
|
|
nodeunit会自动给传入它的对象中引入assert模块。
nodeunit提供test.epect验证断言执行数量是否符合预期。
15.2 mocha
只支持串行测试,默认2s的timeout,并行请用vows:
BDD风格:describe,it,before,after,beforeEach,afterEach.
TDD风格:suite,test,setup,teardown替换上述
执行mocha,会执行./test目录下的javascript文件。
- BDD风格
|
|
- TDD风格
|
|
- 测试异步
增加done()
|
|
15.3 vows
支持并行测试
|
|
如果你想把这段代码放到测试文件夹下,放在可以由Vows测试运行期运行,
run.()改成export(module);
然后
vows test/*
15.4 should.js
断言库,它有一个Object .prototype属性:可以写表达能力很强的断言。
15.5 Tobi和Soda
Tobi模拟浏览器测试,可以结合should.js
Soda远程控制真实的浏览器:
16、使用EJS过滤器处理模板数据
格式
<%=:用在转义的EJS输出上的过滤器
<%-:用在非转义的EJS输出上的过滤器
例子
|
|
看来上就是linux的管道符处理,
各种常用处理
- (1)处理选择:last,first,get:N
- (2)处理大小写:capitalize把第一个字母变大写,还有upcase,downcase
- (3)处理文本:把文本截成一定数量的单词truncate:20,替换replace:’A’,’B’,
排序sort,sort_by:’name’,其中sort返回的是对象,要返回属性的话:|get:’name’ - (4)map:不用sort_by,再get。直接用map创建一个包含对象属性的数组,
map ’name’| sort|
其他模板引擎:
Hotgan:实现mustache语法。
Jade:特点是空格的作用,缩进表示HTML的嵌入关系。
17、fs.watchfile()与fs.watch()
fs.watchfile()与fs.watch()是 Node.js中的两个监测文件API。
- 比较老的是fs.watchFile,使用轮询的方式检查文件,不断的stat(),比较mtime时间戳.
|
|
- 新的api是fs.watch(),根据平台本地的文件修改通知API监测文件,性能更优,但是不如watchFile可靠。在OSX监测目录不会报告参数filename,其他见: 档http://nodejs.org/api/fs.html#fs_caveats
18、Process模块
- process.argv 存储了Node运行当前脚本时传入的参数
- process.env 获取或设定环境变量
- process不是eventEmitter实例,却可以发出exit和uncaughtException事件
注意点:
其中exit是在事件循环(event loop)停止之后才激发的,所以你没有机会在exit事件启动任何异步任务。
18.1 Process的信号处理
UINX有信号的概念,是进程间通信(IPC)的基础形式,它是一组固定的名称,不能传递参数。
信号举例如下
- SIGUSR1:Node进入它内置的调试器
- SIGWINCH:调试终端大小,由shell发送
- SIGINT:ctrl+c,由shell发送,Node默认行为是杀死进程。如果你希望在杀掉服务器前,完成所有连接的处理,可以
|
|
- 还有SIGUSR2和SIGKILL等等
19、子进程
在NODE中创建子进程三种
- 高层api,exec:在回调中创建命令并缓存结果的高层api。
- 底层api,spawn:将单例命令创建Child-Process对象中的底层API。
- 内置的特殊IPC通道fork:用内置的IPC通道创建额外Node进程的特殊方法。
三者比较
cp.exec():只关心结果,不用从子进程的stdio流中访问数据(IRC协议模块有很多,irc,irc-js等等),结果需要转义,可以用execFile()
cp.spawn():返回一个可以交互的ChildProcess对象,允许你跟每个子进程的stdio流交互。(node-cgi范例模块)
cp.fork():也返回一个ChildProcess对象,区别是这个API是用IPC通道添加的, 子进程现在有一个child.send(message) 函数,并且用fork() 调用的脚本能够监听process.on(‘message’) 事件。fork出来的子进程可以参与运算。
20、其他推荐的社区模块
- 表单提交:foridable。
- redis:hiredis,升级node时候,需要重新编译一下hiredis,npm rebuild hiredis。
- mongodb:mongoose,使用时有个{safe:ture}选项表明你想让数据库操作在执行回调之前完成。
参考:
《Node.js 实战》:http://book.douban.com/subject/25870705/
《Node.js in action》:http://book.douban.com/subject/6805117/
(转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)