月度归档:2014年10月

node.js常用模块

前100个依赖库里,underscore是属于语言基础类,express是服务器框架,有5、6个测试框架、以及5、6个递归遍历目录树的工具类、3 个左右的命令行解析工具类,3个主流数据库的链接client库,2个ws库,多个js/CSS前端处理工具链,3-4个promise库,多个模版库, 多个jquery/cavans/dom模拟器以及解析器,一个静态分析器,两个验证库

总得来说,node.js的前100个流行模块应该就可以代表node.js解决问题的问题域
===============================================
1、underscore 3820 packages
提供set的交、并、差、补,提供简单的模版算法,提供各种排序。

2、async 2912 packages
异步库,具体说起来可以用一本书来说

3、request 2474 packages
HTTP请求库,缓存、并发、多客户端,写client以及各种爬虫都会依赖的库

4、optimist 1831 packages
命令行解析库

5、express 1821 packages
http server?过于低级,还是用一下express,能让你的生命美好一些,中等复杂度

6、commander 1692 packages
类似于optimist

7、coffee-script 1620 packages
coffee-script

8、colors 1290 packages
unix终端下显示颜色的库,利于调试和一些特殊场景

9、mkdirp 910 packages
一次性建立目标文件夹,而不是mkdir…cd…mkdir…cd…mkdir这样的模式

10、lodash 901 packages
类似于underscore,更轻量级,更快

11、uglify-js 804 packages
js的压缩器

12、jade 730 packages
express的一个主要模板引擎

13、socket.io 706 packages
webscoket通讯,node.js实现的准官配

14、connect 689 packages
express中间件

15、redis 669 packages
redis的client

16、debug 642 packages
debug辅助模块

17、q 595 packages
异步promise库

18、mime 552 packages
MIME处理库

19、glob 542 packages
通配符文件列表模块

20、node-uuid 527 packages
生成uuid的模块

21、moment 491 packages
时间处理模块,生成类似于:发表于12分钟前这类的string

22、winston 444 packages
调试、log类模块

23、through 442 packages
对stream的封装类

24、ejs 423 packages
express的另一个模板类

25、mongodb 421 packages
mogondb的client

26、mongoose 393 packages
mogondb的client

27、grunt 374 packages
前端构建工具

28、less 353 packages
前端构建工具

29、stylus 346 packages
前端构建工具

30、xml2js 339 packages
较为严格的将xml=>js对象的类

31、cheerio 338 packages
jquery的node.js轻量级实现

32、handlebars 337 packages
Mustache无逻辑模版语言的实现

33、semver 327 packages
npm以及package.json解析版本号时的辅助模块,更为语义化

34、jsdom 324 packages
cheerio的重量级严格实现

35、marked 323 packages
markdown实现

36、wrench 314 packages
递归文件、文件夹操作一体化解决方案

37、pkginfo 300 packages
包信息解析器

38、yeoman-generator 293 packages
yeoman的生成器

39、mocha 287 packages
mocha测试框架

40、rimraf 276 packages
rm -rf

41、underscore.string 274 packages
unserscore的string扩展

42、js-yaml 219 packages
yaml操作类

43、backbone 217 packages
backbone框架

44、browserify 203 packages
js压缩器

45、esprima 197 packages
ECMAScript解析器

46、nopt 197 packages
opt解析

47、mysql 193 packages
mysql的client

48、superagent 182 packages
http request库

49、ws 179 packages
webscoket库

50、oauth 173 packages
oauth认证库

51、readable-stream 173 packages
stream处理库

52、cli-color 171 packages
color库

53、prompt 171 packages
提示符库

54、http-proxy 168 packages
http的一个proxy

55、minimatch 168 packages
通配符实现

56、fs-extra 167 packages
文件操作相关工具库

57、hiredis 167 packages
c的redis client,官配库hiredis的node绑定,redis库可选安装,自动使用

58、jquery 164 packages
jquery实现

59、nconf 164 packages
conf,配置文件管理库

60、should 162 packages
测试框架should

61、passport 159 packages
认证类集合工具库

62、validator 158 packages
后端验证库

63、nodemailer 153 packages
邮件库

64、eventemitter2 152 packages
事件库

65、qs 148 packages
querystring

66、clean-css 147 packages
css库

67、temp 145 packages
临时文件操作库

68、requirejs 142 packages
加载辅助库

69、step 141 packages
异步串行化

70、npm 140 packages
npm

71、when 138 packages
又一个promise库

72、mustache 137 packages
mustache模版库

73、inherits 136 packages
继承工具库

74、shelljs 134 packages
shell化

75、socket.io-client 134 packages
socket.io的node client

76、watch 128 packages
watch库

77、xtend 128 packages
扩展js object的工具类库,兼容各种游览器

78、passport-oauth 127 packages
认证类库

79、nib 124 packages
Stylus工具

80、bindings 123 packages
绑定类库时的帮助类

81、vows 122 packages
异步测试框架vows

82、dateformat 121 packages
处理各类日期的函

83、formidable 121 packages
处理form的工具类

84、chai 120 packages
测试框架

85、log4js 120 packages
日志库

86、pg 118 packages
pg的client

87、tar 116 packages
打包工具类

88、hogan.js 113 packages
mustache编译器

89、canvas 111 packages
canvas的服务端实现

90、ncp 109 packages
递归文件拷贝

91、consolidate 108 packages
模版类

92、event-stream 105 packages
事件类,stream辅助

93、knox 103 packages
Amazon的S3 client

94、sprintf 103 packages
sprintf的node.js版

95、findit 102 packages
递归遍历目录树工具类

96、jshint 102 packages
js静态分析器

97、required-keys 102 packages
js object,key检查器

98、escodegen 100 packages
ECMAScript代码生成

99、node-static 98 packages
静态文件服务器

100、nodeunit 98 packages
单元测试框架

nodejs express route 的用法

1. 首先是最基本的用法。

1
2
3
4
5
var
app=require(
'express'
).createServer();                                                    
app.get(
"/"
,
function
(req,res){                                                    
    
res.send(
"hello world"
);                                                    
});                                                    
app.listen(3000);

当用户访问 127.0.0.1:3000的时候,页面会输出hello world

2. 加个路径试试。

1
2
3
4
5
6
7
var
app=require(
"express"
).createServer();                                               
                                                                                                 
app.get(
"/toolmao"
,
function
(req,res){                                               
    
res.send(
"welcome to toolmao"
);                                               
});                                               
                                                                                                 
app.listen(3000);

当用户访问 127.0.0.1:3000/toolmao的时候,就会输出welcome to toolmao

3. 更为复杂一点的,可以把路径作为参数。

1
2
3
4
5
6
7
var
app=require(
"express"
).createServer();                                           
                                                                                         
app.get(
'/user/:id'
,
function
(req, res){                                           
    
res.send(
'user '
+ req.params.id);                                           
});                                           
                                                                                         
app.listen(3000);

当用户访问 127.0.0.1:3000/user/gainover 的时候,就会输出 user gainover

4. 3中的代码,也可以写为正则表达式的形式。

1
2
3
4
5
6
7
var
app=require(
"express"
).createServer();                                       
                                                                                 
app.get(/\/user\/([^\/]+)\/?/,
function
(req, res){                                       
    
res.send(req.params);                                       
});                                       
                                                                                 
app.listen(3000);

这里可以根据你的需要进行正则的自定义。正则中的匹配结果,存储在req.params参数中。

一个更复杂的正则的例子,如下:含有2个匹配。

1
2
3
app.get(/^\/users?(?:\/(\d+)(?:\.\.(\d+))?)?/,
function
(req, res){                                  
    
res.send(req.params);                                  
});

请求时,输出如下:

1
2
3
4
5
6
7
8
$ curl http:
//dev:3000/user                                
[
null
,
null
]                                
$ curl http:
//dev:3000/users                                
[
null
,
null
]                                
$ curl http:
//dev:3000/users/1                                
[
"1"
,
null
]                                
$ curl http:
//dev:3000/users/1..15                                
[
"1"
,
"15"
]

5. 如果我们想指定参数为id,同时又想用正则进行限制,可以写为:

/user/:id([0-9]+)

—————————————————————————-

Route的依次执行

1. 当一个请求,能够匹配到多个route时,我们可以调用内置的next函数,来依次进行处理。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
app.get(
'/users/:id?'
,
function
(req, res, next){                       
    
var
id = req.params.id;                       
    
if
(id) {                       
        
// do something                       
    
}
else
{                       
        
next();                       
    
}                       
});                       
                                                 
app.get(
'/users'
,
function
(req, res){                       
    
// do something else                       
});

当用户请求,/users/gainover时,可以进行某种处理,而当用户请求为/users/, id 不存在,则会调用next()函数,进而调用 app.get(“/users/”, ….);

2. 一个route里可以有多个处理函数。例如:

app.get(‘/users/:id/edit/’,function1,function2,…);

一个实际的例子可能如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function
loadUser(req, res, next) {           
  
// You would fetch your user from the db           
  
var
user = users[req.params.id];           
  
if
(user) {           
    
req.user = user;           
    
next();           
  
}
else
{           
    
next(
new
Error(
'Failed to load user '
+ req.params.id));           
  
}           
}           
function
andRestrictToSelf(req, res, next) {           
  
req.authenticatedUser.id == req.user.id           
    
? next()           
    
: next(
new
Error(
'Unauthorized'
));           
}           
                         
app.get(
'/user/:id/edit'
, loadUser, andRestrictToSelf,
function
(req, res){           
  
res.send(
'Editing user '
+ req.user.name);           
});

当用户访问:/user/gainover/edit时,首先会调用第一个处理函数loadUser,判断用户是否存在于users中,如果不存 在,通过next(new Error(msg)); 的方式抛出异常,否则,执行next(),而next此时实际就是指向 andRestrictToSelf 函数,然后判断当前登录的id和被编辑的id是否等同,如果等同,则继续next(),从而执行 res.send( …);

引用:http://node-js.diandian.com/post/2012-07-03/40029072624

浅谈 Express 4.0 Router 模块

Express 是目前 node 社区最主要的 Web 框架,前不久刚刚升级到了 4.0 版本。与 3.x 版本比,4.0 版本拥有一个全新设计的 Router 模块,开发者可以更方便的对 middleware 进行隔离与重用。

Express 3.x 时代的中间件 (middleware) 与控制器 (controller)

在 express 3.x 版本中,一个控制器往往不是业务逻辑的全部,中间件才是业务逻辑的大头。例如一个处理用户订单的服务,往往验证用户权限、读写数据库等主要逻辑工作都在中间件中就完成了,而控制器所做的大部分工作就是拼数据给视图 (view)。

一个标准的 URL 映射写法如下:

app.js

1
2
3
4
5
// 这里是一些中间件,express 的常见写法
var A = require('./middlewares/A');
var B = require('./middlewares/B');
var C = require('./middlewares/C');
app.get('/books', A, B, C, require('./controllers/book').index);
controllers/book.js

1
2
3
4
5
6
7
exports.index = function (req, res) {
    var retA = req.A; // 中间件 A 的输出结果
    var retB = req.B; // 中间件 B 的输出结果
    var retC = req.C; // 中间件 C 的输出结果
    // ... 其余程序逻辑
}
// ...

控制器与中间件的相互依赖关系是完全隐式的,不能通过代码分析来得到任何的保证。中间件的插入与控制器的代码被分离在了

app.js

controllers/book.js

两个文件中,不仅阅读起来不直观,修改起来也很容易出错。假如有一天团队的新同事修改了

book.js

去掉了中间件

C

的逻辑,但是忘记了修改

app.js

(这是一个非常容易犯的错误),那么 express 不会报任何错误,code review 也很难发现,因为这两处代码离得实在是太远了。

这种设计同时会导致测试非常困难,因为单独

require

一个控制器是毫无意义的,因为控制器本身不可能独立于中间件来执行。如果要测试控制器,要么在单元测试代码在控制器前里现场装配中间件,要么就 mock 请求通过中间件后的数据。无论哪种做法,都需要单元测试完全了解中间件与控制器的业务逻辑才可能实现,这样就增大了单元测试的难度。

一个解决方法是将二者的依赖关系倒置,把控制器模块写成一个接收

app

作为参数的函数,在函数内部装配中间件与控制器。代码如下:

app.js

1
require('./controllers/book')(app);
controllers/book.js

1
2
3
4
5
6
7
8
9
10
11
12
13
var A = require('../middlewares/A');
var B = require('../middlewares/B');
var C = require('../middlewares/C');

module.exports = function (app) {
    app.get('/books', A, B, C, function (req, res) {
        var retA = req.A; // 中间件 A 的输出结果
        var retB = req.B; // 中间件 B 的输出结果
        var retC = req.C; // 中间件 C 的输出结果
        // ... 其余程序逻辑
    });
};
// ...

这样做虽然提高了代码的内聚性,但是直接把

app

暴露给其它模块使得

app

有被滥用的风险,让二者从面向接口的松散耦合变成了直接操纵实例的强耦合。同时这种方案不仅没有提高可测试性,反而大大提高了单元测试的难度(想想现在都需要 mock 一个

app

了 T_T)。

为了更好的解决这个问题,Express 4.0 给出了更好的解决方式:

express.Router

使用

express.Router

来组织控制器与中间件

express.Router

可以认为是一个微型的只用来处理中间件与控制器的

app

,它拥有和

app

类似的方法,例如

get

post

all

use

等等。上面的例子使用

express.Router

可以修改为:

app.js

1
app.use(require('./controllers/book'));
controllers/book.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var router = require('express').Rouer(); // 新建一个 router

var A = require('../middlewares/A');
var B = require('../middlewares/B');
var C = require('../middlewares/C');

// 在 router 上装备控制器与中间件
router.get('/books', A, B, C, function (req, res) {
    var retA = req.A; // 中间件 A 的输出结果
    var retB = req.B; // 中间件 B 的输出结果
    var retC = req.C; // 中间件 C 的输出结果
    // ... 其余程序逻辑
});

// ...

// 返回 router 供 app 使用
module.exports = router;

通过

express.Router

,控制器与中间件的代码紧密的联系在一起,并且避免了传递

app

的潜在风险。同时,一个

router

就是一个完整的功能模块,不需要任何装配就可以执行。这一点对于单元测试来说非常简单。

express.Router

的其他特性

中间件重用

上面提到过,

express.Router

可以认为是一个迷你的

app

,它拥有一个独立的中间件队列。这个特性可以用来共享一些常用的中间件,例如:

express 3.x

1
2
3
4
5
6
// parseBook 是个中间件
app.get('/books/:bookId', parseBook, viewBook);
app.get('/books/:bookId/edit', parseBook, editBook);
app.get('/books/:bookId/move', parseBook, moveBook);

app.get('/other_link', otherController);

Express 4.0 的写法:

express 4.0

1
2
3
4
5
6
7
8
9
10
var bookRouter = express.Router();
app.use('/books', bookRouter);

bookRouter.use(parseBook);
// 下面三个控制器都会经过 parseBook 中间件
bookRouter.get('/books/:bookId', viewBook);
bookRouter.get('/books/:bookId/edit', editBook);
bookRouter.get('/books/:bookId/move', moveBook);

app.get('/other_link', otherController); // 不会经过 parseBook 中间件

这个例子中

bookRouter

使

parseBook

这个中间件得到了充分的重用。

搭建 rest-ful 服务

Code talks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var bookRouter = express.Router();
bookRouter
    .route('/books/:bookId?')
    .get(function (req, res) {
        // ...
    })
    .put(function (req, res) {
        // ...
    })
    .post(function (req, res) {
        // ...
    })
    .delete(function (req, res) {
        // ...
    })

小节

express.Router

是 express 4.0 中我最喜欢的更新,我认为 express 4.0 的代码里应该尽量多使用

express.Router

来代替原先的

app.get

方式。原先 URL 路径、中间件、控制器三者的松散关系可以借由

express.Router

变得紧密,整个控制器变成了一个不依赖于任何外部实例的独立模块,更有利于模块的拆分(想想把网站的各个模块都拆成独立的 router 吧),同时对于测试也更加友好。

引用:http://lostjs.com/2014/04/24/router-in-express-4/