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

本文翻译自: node

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

  

一、介绍

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

 

  本文的目的并非打算去说服你们去使用其中的任何一款框架,而是但愿可以帮助你们去对比分析这三个框架的优劣势。github

  

二、框架背景

 

  今天咱们对比的这三款框架其实都有不少的共通点。好比他们均可以几行代码就能建立一个服务,并且进行REST API的开发也是小菜一碟。下面咱们就分别来看这三款框架吧。web

 

2.一、Express

 

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

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

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

 

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次的代码提交。app

 

2.三、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

 

三、建立服务 

  基本上每一个刚开始接触Node.js的开发者第一步操做就是建立一个服务。由于下面咱们将依次使用每一个框架来分别建立一个服务,来看看他们之间的类似处与不一样的地方。

 

3.一、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.二、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.三、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本身实现的逻辑。

 

四、路由

  接下来咱们继续深刻了解做为一个服务的一个重要功能,那就是路由。第一步咱们将使用每一个框架来分别建立一个“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.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的话,你可能要投入更多精力进行开发,而不是简单的调用一个第三方中间件。

 

六、总结

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

相关文章
相关标签/搜索