本文翻译自:

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应该是大型项目组或者大型项目的第一选择。他所倡导的配置优于代码会使得项目组 在代码的重复性上受益不浅,这也正是大多数项目组所追求的目标。现在行动起来,尝试一款新的框架吧,可能你会喜欢他也可能会讨厌他,但如果不去尝试你永远也不会知道结果是什么,最终所有的这些经历都会让你成长为一个更加优秀的开发者。

[译]Node.js框架对比:Express/Koa/Hapi的更多相关文章

  1. Node.js 框架对比之 Express VS Koa

    背景 上图是一个典型的采用 Node.js 开发 web 应用的前后端结构,下面介绍一下 Node 服务层在其中的作用以及使用 Node.js 的一些优劣. Node 服务层作用: 请求代理 传统做法 ...

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

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

  3. 13 款惊艳的 Node.js 框架——第2部分

    [编者按]本文作者为 Peter Wayner,主要介绍13款至精至简的 Node.js 框架,帮助你简化高速网站.丰富 API 以及实时应用的开发流程.本文系国内 ITOM 管理平台 OneAPM ...

  4. node.js框架express的安装

    node.js框架express的安装 首先假定你已经安装了 Node.js,接下来为你的应用创建一个目录,然后进入此目录并将其作为当前工作目录. $ mkdir myapp $ cd myapp 通 ...

  5. Express/Koa/Hapi

    Express/Koa/Hapi 本文翻译自: https://www.airpair.com/node.js/posts/nodejs-framework-comparison-express-ko ...

  6. 不可错过的Node.js框架

    前言 Node.js是由Ryan Dahl于2009年创建的.它是一个开源的跨平台运行时环境,用于开发服务器端和网络应用程序,它是基于Google Chrome V8 JavaScript引擎构建的. ...

  7. 【转】node.js框架比较

    我偶然间看到这篇文章,转这个文章并没有什么含义,仅仅是感觉总结的不错,对于新学node的友友们来说希望这篇文章为大家对 Node.js 后端框架选型带来一些帮助,学习不再迷茫,也是让我有个保存,以后参 ...

  8. 13 款惊艳的 Node.js 框架——第1部分

    [编者按]本文作者为 Peter Wayner,主要介绍13款至精至简的 Node.js 框架,帮助你简化高速网站.丰富 API 以及实时应用的开发流程.本文系国内 ITOM 管理平台 OneAPM ...

  9. Node.js 框架

    Node.js的是一个JavaScript平台,它允许你建立大型的Web应用程序.  Node.js的框架平台使用JavaScript作为它的脚本语言来构建可伸缩的应用. 当涉及到Web应用程序的开发 ...

随机推荐

  1. 20175316 盛茂淞 实验一 Java开发环境的熟悉

    20175316 盛茂淞 实验一 Java开发环境的熟悉 实验目的 使用JDK编译.运行简单的Java程序 实验要求 1.建立"自己学号exp1"的目录 2.在"自己学号 ...

  2. Java程序员职业生涯规划

    一.规划 工作3年了,感觉自己的技术现在到了一个瓶颈,在做一些重复性的业务性的工作,没有长进,提高太慢:因此停下脚步对自己的职业生涯做了一个规划,并为之努力奋斗: 20-27岁:技术积累阶段在这 5 ...

  3. [算法专题] 二分搜索&排序数组

    基础知识 二分非递归写法: int binary_search(const int a[], const int size, const int val) { int lower = 0; int u ...

  4. 程序猿的日常——工作中常用的Shell脚本

    工作当中总是会有很多常用的linux或者命令,这里就做一个总结 文件远程拷贝 如果想把文件从本机拷贝到远程,或者从远程下载文件到本地. # 把本地的jar拷贝到远程机器xxxip的/home/sour ...

  5. 从零开始的程序逆向之路基础篇 第二章——用OllyDbg(OD)分析一个简单的软件

    作者:Crazyman_Army 原文来自:https://bbs.ichunqiu.com/thread-43469-1-1.html 0x00知识回顾 (由于笔者省事,没开XP虚拟机,而且没关闭A ...

  6. typescript handbook 学习笔记3

    概述 这是我学习typescript的笔记.写这个笔记的原因主要有2个,一个是熟悉相关的写法:另一个是理清其中一些晦涩的东西.供以后开发时参考,相信对其他人也有用. 学习typescript建议直接看 ...

  7. iptables概述

    [参考文章]:iptables详解-朱双印个人日志 [参考文章]:linux iptables详解--个人笔记 [参考文章]:iptables详解--转 [参考文章]:linux下IPTABLES配置 ...

  8. 如何用java POI将word中的内容导入到mysql数据库中

    由于作业需要,要求我们将word文档中的数据直接导入到mysql中,在网上找了很常时间,终于将其解决. 由于比较初级,所以处理的word文档是那种比较规范的那种,条例比较清晰,设计的思路也比较简单,就 ...

  9. (转)Linux开启路由转发功能

    原文:https://www.linuxidc.com/Linux/2016-12/138661.htm 标记一下,今天想让一台Red Hat Enterprise Linux 7开通iptables ...

  10. linux下配置nginx反向代理例子

    官方说明: 例子: 虚拟机ip:192.168.85.3,物理机VMware Network Adapter VMnet8  ip:192.168.85.1 1,准备tomcat 准备一tomcat, ...