Express/Koa/Hapi

本文翻译自:

https://www.airpair.com/node.js/posts/nodejs-framework-comparison-express-koa-hapi

1、介绍

  直至今日,Express.js仍然是最为流行的Node.js Web应用程序框架。它似乎已经逐渐成为大多数Node.js Web应用程序的基础依赖框架,包括很多流行的框架,比如Sail.js就是以Express.js为基础搭建的。然而现在我们有了更多“类sinatra”(注:sinatra是一款Ruby框架,代码非常简洁,号称开发一个博客项目只需要100行代码)似的框架可以选择。也就是接下来我们将分别介绍的Koa和Hapi两个框架。

  本文的目的并不是打算去说服大家去使用其中的任何一款框架,而是希望能够帮助大家去对比分析这三个框架的优劣势。

2、框架背景

  今天我们对比的这三款框架其实都有很多的共通点。比如他们都可以几行代码就能创建一个服务,而且进行REST API的开发也是小菜一碟。下面我们就分别来看这三款框架吧。

2.1、Express

  2009年6月26日,TJ Holowaychuk 第一次提交了Express的代码。在2010年1月2日,Express正式发布了0.0.1版本,截止当时,作者已经提交了超过660次代码。当时Express的两位主要开发维护者分别是TJ 以及 Ciaron Jessup。第一版发布的时候,Express在Github的readme.md介绍文件中式这么描述这块框架的:

一款基于node.js以及Chrome V8引擎,快速、极简的JS服务端开发框架。

  5年多后今天,Express目前已经发布到4.10.1版本,提交超过4925次代码,目前主要是采用StrongLoop进行开发维护,因为TJ同学已经转入GO语言开发社区了。

2.2、Koa

  Koa是在一年以前也就是在2013年8月17日由TJ同学(是的,还是他...)首次提交的代码。他当时是这么描述Koa的:“更具有表现力,更健壮的Node.js中间件。基于co组件的generators处理异步回调,无论是Web应用还是REST API开发,你的代码都将变得更加优雅”。(注:Koa2发布后,已经放弃了引入co组件,而是开始采用ES7的async/await语法处理异步回调)。轻量化的Koa号称不超过400行代码。(注:SLOC是源代码行数,又分为物理代码行数LOC,以及逻辑代码行数LLOC)。截止目前,Koa已经发布了0.13.0版本,超过585次的代码提交。

2.3、Hapi

  Hapi是由来自于沃尔玛实验室的Eran Hammer同学在2011年8月5日首次提交的。原本他只是Postmile(这是一款在node.js上开发的协作列表工具,服务端由Hapi完成的一个核心部件,同样也是基于Express开发。后来Hapi才被独立出来作为一款框架进行开发维护,Eran同学在他的博客里这样说道:

  “Hapi的核心思想是配置优于代码,所以业务代码必须从传输层中剥离出来”

  至今为止,Hapi已经提交超过3816次代码,版本是7.2.0,当前仍然是由Eran Hammer进行主要开发维护。

  OK,最后让我们来通过社区的统计数据来看看这三个框架的活跃程度:

  

参考项

Express.js

Koa.js

Hapi.js

Github点赞数

16158

5846

3283

代码贡献者

163

49

95

依赖包数量

3828

99

102

StackOverFlow提问数

11419

72

82

3、创建服务

  基本上每个刚开始接触Node.js的开发者第一步操作就是创建一个服务。因为下面我们将依次使用每个框架来分别创建一个服务,来看看他们之间的相似处与不同的地方。

3.1、Express

var express = require('express');
var app = express(); var server = app.listen(3000, function() {
console.log('Express is listening to http://localhost:3000');
});

  上面的操作对于大多数Node开发者来说应该都是很熟练了。我们先引入express,然后创建一个实例对象并将其赋值给变量app。接下来是实例化一个服务,并且开始监听3000端口。app.listen() 其实就是对nodejs原生的http.createServer()进行了一层封装。

3.2、Koa

var koa = require('koa');
var app = koa(); var server = app.listen(3000, function() {
console.log('Koa is listening to http://localhost:3000');
}); 

  显而易见,Koa的语法和Express非常相似。其实来说你只需要将引入express修改为引入koa即可。同样的,app.listen() 也是对http.createServer()进行了一层封装。

3.3、Hapi

var Hapi = require('hapi');
var server = new Hapi.Server(3000); server.start(function() {
console.log('Hapi is listening to http://localhost:3000');
});

  Hapi的语法比较特别一些。不过,第一步还是引入hapi,但是这里是实例化存入一个hapi app变量中,然后就可以创建一个指定端口的服务了。在Express和Koa中这一步我们得到的是一个回调函数,但是Hapi返回的是一个server对象。一旦我们通过server.start()来调用这个在3000端口的服务以后,他将会返回一个回调函数。然后跟Koa和Express不一样的地方在于,这个回调并不是对http.CreateServer()进行的一层封装,而是Hapi自己实现的逻辑。

4、路由

  接下来我们继续深入了解作为一个服务的一个重要功能,那就是路由。第一步我们将使用每个框架来分别创建一个“Hello World”应用,然后再继续关注一些更实用的功能,REST API。

4.1 Hello World

4.1.1  Express

var express = require('express');
var app = express(); app.get('/', function(req, res) {
res.send('Hello world');
}); var server = app.listen(3000, function() {
console.log('Express is listening to http://localhost:3000');
});

  我们使用get()方法来捕获“GET /”请求,然后调用一个回调函数来处理请求,该回调函数拥有两个参数:req与res。在这个例子中我们仅仅使用了res的res.send()方法来向页面返回一个字符串。Express包含了很多内置的方法来处理路由功能。下面是几个Express中常用的方法(只是部分,并不是全部方法):get, post, put, head, delete…

4.1.2 Koa

var koa = require('koa');
var app = koa(); app.use(function *() {
this.body = 'Hello world';
}); var server = app.listen(3000, function() {
console.log('Koa is listening to http://localhost:3000');
});

  Koa和Express有些许的不同之处,因为他使用了ES6 的generators语法。(注:generators是ES6提出的一种异步回调的解决方法,在ES7中将直接升级为async/await)在方法前面加上一个 * 表示该方法返回一个generator对象。generators函数的作用就是使得异步函数产生一些同步的值,但是这些值仍然是在当前的请求范围之类。(注:generator对通过yield 定义不同的状态值,return也算是一个状态值。详情了解:http://es6.ruanyifeng.com/#docs/generator )在app.use()中,generator函数对响应体进行赋值。在Koa中this对象,其实就是对node的request与response对象进行的封装。this.body在Koa中是一个响应体对象的方法。它基本上能被赋值为任何值,字符串、buffer、数据流、对象或者是null。Koa核心库提供了很多中间件,这里我们只是使用了其中的一个,这个中间件可以捕获所有的路由,然后响应一个字符串。

4.1.3 Hapi

var Hapi = require('hapi');
var server = new Hapi.Server(3000); server.route({
method: 'GET',
path: '/',
handler: function(request, reply) {
reply('Hello world');
}
}); server.start(function() {
console.log('Hapi is listening to http://localhost:3000');
});

  这里我们使用了由server对象提供的一个内置方法:server.route(),这个方法需要这些参数:path(必填)、method(必填)、vhost以及handler(必填)。这个HTTP方法可以处理我们常见的GET/PUT/POST/DELETE请求,也可以使用*来处理所有路由请求。回调函数会被Hapi默认传入request对象以及reply方法,reply是必须被执行的方法,而且需要传入一项数据,这个数据可以是字符串、序列化的对象或者流。

4.2 REST API

  Hello World程序从来都没有太多的期望,因为它只能展示创建及运行一个应用最基本最简单的操作。REST API几乎是所有大型应用程序所必须的一个功能,同时对于我们更好的理解这些框架有很大的帮助。因此接下来我们将看看这几个框架是如何来处理REST API。

4.2.1 Express

var express = require('express');
var app = express();
var router = express.Router(); // REST API
router.route('/items')
.get(function(req, res, next) {
res.send('Get');
})
.post(function(req, res, next) {
res.send('Post');
}); router.route('/items/:id')
.get(function(req, res, next) {
res.send('Get id: ' + req.params.id);
})
.put(function(req, res, next) {
res.send('Put id: ' + req.params.id);
})
.delete(function(req, res, next) {
res.send('Delete id: ' + req.params.id);
}); app.use('/api', router); // index
app.get('/', function(req, res) {
res.send('Hello world');
}); var server = app.listen(3000, function() {
console.log('Express is listening to http://localhost:3000');
});

  我们在现有的Hello World程序上增加了REST API。Express提供了一些缩写的方法来处理路由。这是Express 4.x 版本的语法,其实跟Express 3.x 版本差不多,同样希望你不再使用express.Router()方法,而是换成新的API:app.use('/api', router)。新的API可以让我们使用app.route()来替换之前的router.route(),当然了需要添加一个描述性的动词/api.这是一个不错的修改,因为降低开发者出现错误的机会,同时对原有的HTTP方法进行了最小的一个修改。

4.2.2 Koa

var koa = require('koa');
var route = require('koa-route');
var app = koa(); // REST API
app.use(route.get('/api/items', function*() {
this.body = 'Get';
}));
app.use(route.get('/api/items/:id', function*(id) {
this.body = 'Get id: ' + id;
}));
app.use(route.post('/api/items', function*() {
this.body = 'Post';
}));
app.use(route.put('/api/items/:id', function*(id) {
this.body = 'Put id: ' + id;
}));
app.use(route.delete('/api/items/:id', function*(id) {
this.body = 'Delete id: ' + id;
})); // all other routes
app.use(function *() {
this.body = 'Hello world';
}); var server = app.listen(3000, function() {
console.log('Koa is listening to http://localhost:3000');
}); 

  很明显,Koa并不能像Express那样去降低route动词的重复性。它同时还需要引入一个独立的中间件来处理路由。我选择使用koa-route,是因为他主要是由Koa小组来开发维护,当然也还有很多其他开发者贡献的路由中间件可以选择。从方法名的关键字上来看,koa的路由和express也是非常相似的,例如.get(), .put(), .post(), 以及 .delete()。

  Koa在处理路由有一个优势,它使用了ES6 的generator函数,从而降低了回调函数的复杂度。

4.2.3  Hapi

var Hapi = require('hapi');
var server = new Hapi.Server(3000); server.route([
{
method: 'GET',
path: '/api/items',
handler: function(request, reply) {
reply('Get item id');
}
},
{
method: 'GET',
path: '/api/items/{id}',
handler: function(request, reply) {
reply('Get item id: ' + request.params.id);
}
},
{
method: 'POST',
path: '/api/items',
handler: function(request, reply) {
reply('Post item');
}
},
{
method: 'PUT',
path: '/api/items/{id}',
handler: function(request, reply) {
reply('Put item id: ' + request.params.id);
}
},
{
method: 'DELETE',
path: '/api/items/{id}',
handler: function(request, reply) {
reply('Delete item id: ' + request.params.id);
}
},
{
method: 'GET',
path: '/',
handler: function(request, reply) {
reply('Hello world');
}
}
]); server.start(function() {
console.log('Hapi is listening to http://localhost:3000');
});

  跟其他框架相比,Hapi的路由配置给人的第一印象就是代码清爽,可读性高。甚至连必填的配置参数method,path,hanlder以及reply都非常容易辨别。跟Koa一样,Hapi路由的代码重复性也比较高,所以出错的几率也比较大。之所有这么做,是因为Hapi更希望使用配置来完成路由,这样我们的代码会更清爽,在小组内也会更容易的维护。Hapi同样试图去提高代码错误处理能力,因为有的时候他甚至不需要开发者编写任何代码(注:意思是完全都过配置实现,回调函数也是用默认的。这样出错的 概率就了很多,也更容易上手)。如果你试图去访问一个没有在REST API中定义的路由,那么Hapi将会返回一个包含状态值与错误信息的JSON对象。

5、优劣势

5.1 Express

5.1.1 优势

  Express拥有最大社区,比仅仅是跟这三个框架相比,而是对于所有的Nodejs框架来说也是最大的。目前来说,他是最为三者中最为成熟的框架,接近5年的开发投入,同时还采用了StrongLoop(注:StrongLoop是一个进程管理工具,提供CLI与UI界面。)对线上仓库的代码进行管理。他提供了一种简单的方式来创建和运行一个服务,同时路由的内置也使得代码得到了重复使用。

5.1.2 劣势

  在使用Express过程中,我们往往要处理很多单调乏味的任务。比如他没有内置的错误处理机制,另外对于同样一个问题可以有很多中间件来供选择,这也使得开发者容易迷失在中间件的选择中,总而言之就是,一个问题你会有N多解决方案。Express声称自己是可配置选择的,这其实不没有好或不好,但是对于一个刚刚接触Express的开发者来说,这就是他的劣势了。另外,Express跟其他的框架相比也还有很大的差距。

5.2 Koa

5.2.1 优势

  Koa的一个小进步就是他的代码比较富有表现力,开发中间件也比其他框架更容易得多。Koa是一个很基础的准系统框架,开发者可以选择(或开发)他们所需要的中间件,而不是去选择Express或Hapi的中间件。他同时也是三者中唯一一个积极拥抱ES6的框架,比如采用了ES6 generators函数。

5.2.2 劣势

  目前Koa还处于不稳定版本,还处在开发阶段。使用ES6进行开发的确是处于领先水平,比如Koa需要基于Nodejs 0.11.9以上的版本运行,而目前nodejs的文本版本是0.10.33。这是一件可以算作好也可以算作不好的事情,就像Express开发者有很多中间件要选择甚至自己开发中间件一样。比如我们在上面看到的一样,对于路由来说就有很多中间件供我们选择。

5.3 Hapi

5.3.1 优势

  Hapi一直很自豪的说他们的框架是配置优于代码,当然也有很多开发者可能会质疑把这一点算作是优势。但这一点对于大型项目组来说,的确是可以保持代码的统一性以及代码复用性。另外这款框架是由沃尔玛实验室支持的,也有很多大公司在线上环境使用Hapi,表明他已经通过了严峻的测试,因为这些公司会考虑得更多才会使用Hapi来运行他们的项目。因此所有的这些迹象都表明Hapi正在朝一个伟大的框架发展。

5.3.2 劣势

  Hapi的定位更倾向于大型或复杂的应用程序。对于一个简单的应用来说,Hapi在代码上反而有些显得冗余了,另外目前Hapi所提供的样例程序也比较少,使用Hapi进行开发的开源应用同样很少。因此,如果选择Hapi的话,你可能要投入更多精力进行开发,而不是简单的调用一个第三方中间件。

6、总结

  我们已经看了三个框架还算不错具有代表性的一些样例代码。Express仍然是当下最为流行,以及最被人所知晓的框架。当开始一个新的开发项目时,可能大家的第一反应就是用Express来创建一个服务。但是现在更希望大家多考虑考虑使用Koa或者Hapi。Koa积极拥抱ES6的语法,展示了promise的真正魅力。目前整个web开发社区也都意识到ES6的优势,正在逐步往上面迁移。Hapi应该是大型项目组或者大型项目的第一选择。他所倡导的配置优于代码会使得项目组 在代码的重复性上受益不浅,这也正是大多数项目组所追求的目标。现在行动起来,尝试一款新的框架吧,可能你会喜欢他也可能会讨厌他,但如果不去尝试你永远也不会知道结果是什么,最终所有的这些经历都会让你成长为一个更加优秀的开发者。

Express/Koa/Hapi的更多相关文章

  1. [转]Node.js框架对比:Express/Koa/Hapi

    本文转自:https://www.cnblogs.com/souvenir/p/6039990.html 本文翻译自: https://www.airpair.com/node.js/posts/no ...

  2. [译]Node.js框架对比:Express/Koa/Hapi

    本文翻译自: https://www.airpair.com/node.js/posts/nodejs-framework-comparison-express-koa-hapi 1.介绍 直至今日, ...

  3. express koa koa2 优缺点分析

    发布日期 2009年6月26日,TJ 提交 Express 第一次 commit.目前拥有 5000 多次 commit. 2013年8月17日, TJ 只身一人提交 Koa 第一次 commit.目 ...

  4. 分别使用http,express,koa请求第三方接口

    今天又再次恶补了一下http的内容,确切地说是node.js里面的http的内容,啊,百度了半天express怎么请求第三方接口,结果发现自己买的入门书籍都有这个内容.舍近求远,我真是醉了.还有百度上 ...

  5. node不懂的方法的使用

    1. 学习的时候注意,过滤器,上传文件sftp,读取excel,还有cookie的操作,sql的操作.node的框架express koa hapi 还有引擎ejs,hbs,jade,日志管理等,并发 ...

  6. 浅谈前端nuxt(ssr)

    SSR: 服务端渲染(Server Side Render),即:网页是通过服务端渲染生成后输出给客户端. 一.那为什么要使用SSR呢? 我用一句话理解的就是降低SPA(Single Page App ...

  7. 用 Nokitjs 解决前端开发中的跨域问题

    问题 在开发一些「单页应用」时,通常会使用 Ajax 和服务器通讯,比如 RESTful API,通常「前端」和「服务端 API」可能是有不同人员在负责,也不在同一个工程下,那么开发过程中就可能会遇到 ...

  8. 好的web前端是如何拿到30万年薪的?

    2018年前端开发不再像过去几年里新技术框架层出不穷,而是各种组件,模块,很多东西都有痕迹可寻,技术都在原来的基础上有了革新和沉淀. 前端招聘方面也在紧跟技术发展,大量“滥竽充数”的速成开发者开始失去 ...

  9. 【转载】Express、Koa、Hapi框架对比

    中文翻译:http://ourjs.com/detail/5490db1c8a34fa320400000e 英文原文:https://www.airpair.com/node.js/posts/nod ...

随机推荐

  1. 获取https证书

    获取证书 个人如果想购买SSL证书,相对来说还是比较简单的.对于小型网站,可以考虑通过StartSSL获取免费证书.另外还可以通过LetsEncrypt项目使用一个简单的命令行界面为服务获取免费证书. ...

  2. 查看Nodejs 占用的端口

    查看 Nodejs 占用的端口 ps -ef | grep node强制关闭端口 kill

  3. 使用OLEDB读取excel和csv文件

    这是我第一次在博客上写东西,简单的为大家分享一个oledb读取文件的功能吧,这两天在做一个文件导入数据库的小demo,就想着导入前先在页面上展示一下,之前调用Microsoft.Office.Inte ...

  4. Part 82 to 85 Talking about Generic queue, stack collection class

    Part 82   Generic queue collection class Part 83   Generic stack collection class Part 84   Real tim ...

  5. ssh-key 与 git账户配置以及多账户配置

    http://www.cnblogs.com/dubaokun/p/3550870.html 在使用git的时候,git与远程服务器是一般通过ssh传输的(也支持ftp,https),我们在管理远程分 ...

  6. Cocos2d-JS中的Sprite精灵类

    精灵类是cc.Sprite,它的类图如下图所示.cc.Sprite类直接继承了cc.Node类,具有cc.Node基本特征. 创建Sprite精灵对象 创建精灵对象可以使用构造函数实现,它们接受相同的 ...

  7. python基础:day3作业

    修改haproxy配置文件 基本功能:1.获取记录2.添加记录3.删除记录 代码结构:三个函数一个主函数 知识点:1.python简单数据结构的使用:列表.字典等 2.python两个模块的使用:os ...

  8. hdu 1318 Palindromes(回文词)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1318 题意分析:输入每行包含一个字符串,判断此串是否为回文串或镜像串. 表面上看这道题有些复杂,如果能 ...

  9. 试写Python内建函数range()

    还没查阅源码,先试着练手 class my_range(object): def __init__(self, *args): if not args: raise TypeError, 'range ...

  10. python——异常处理

    python处理异常的格式一般如下: try: A部分 Except  .... [as ..]: B部分 [ finally: C部分 Else: D部分 ] ps: 1.不管有没有异常,final ...