【轉載】Express、Koa、Hapi框架對比

中文翻譯:http://ourjs.com/detail/5490db1c8a34fa320400000enode

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

1 介紹

Express.js無疑是當前Node.js中最流行的Web應用程序框架。它幾乎成爲了大多數Node.js web應用程序的基本的依賴,甚至一些例如Sails.js這樣的流行的框架也是基於Express.js。然而你還有一些其餘框架的選擇,能夠給你帶來「sinatra」同樣的感受(譯註:sinatra是一個簡單的Ruby的Web框架)。另外兩個最流行的框架分別是Koa和Hapi。github

這篇文章不是打算說服你哪一個框架比另一個更好,而是隻是打算讓你更好地理解每一個框架能作什麼,什麼狀況下一個框架能夠秒殺另一個。web

 

2 框架的背景

咱們將要探討的兩個框架看起來都很是類似。每個都可以用幾行代碼來構建一個服務器,並均可以很是輕易地構建REST API。咱們先瞧瞧這幾個框架是怎麼誕生的。express

2.1 Express

 

2.1 Express

2009年6月26日,TJ Holowaychuk提交了Express的第一次commit,接下來在2010年1月2日,有660次commits的Express 0.0.1版本正式發佈。TJ和Ciaron Jessup是當時最主要的兩個代碼貢獻者。在第一個版本發佈的時候,根據github上的readme.md,這個框架被描述成:api

瘋通常快速(而簡潔)的服務端JavaScript Web開發框架,基於Node.js和V8 JavaScript引擎。服務器

差很少5年的時間過去了,Express擁有了4,925次commit,如今Express的最新版本是4.10.1,由StrongLoop維護,由於TJ如今已經跑去玩Go了。架構

2.2 Koa

大概在差很少一年前的2013年8月17日,TJ Holowaychuk(又是他!)隻身一人提交了Koa的第一次commit。他描述Koa爲「表現力強勁的Node.js中間件,經過co使用generators使得編寫web應用程序和REST API更加絲般順滑」。Koa被標榜爲只佔用約400行源碼空間的框架。Koa的目前最新版本爲0.13.0,擁有583次commits。app

2.3 Hapi

2011年8月5日,WalmartLabs的一位成員Eran Hammer提交了Hapi的第一次commit。Hapi本來是Postmile的一部分,而且最開始是基於Express構建的。後來它發展成本身本身的框架,正如Eran在他的博客裏面所說的:框架

Hapi基於這麼一個想法:配置優於編碼,業務邏輯必須和傳輸層進行分離..

Hapi最新版本爲7.2.0,擁有3,816次commits,而且仍然由Eran Hammer維護。

以下有些社區的統計數據顯示這些framework的流行程度:

衡量維度 Express.js Koa.js Hapi.js
Github Stars 16,158 4,846 3,283
Contributors 163 49 95
依賴的包數量 3,828 99 102
內存溢出問題數量 11,419 72 82

全部開發者要開發Node.js web應用程序的第一步就是構建一個基本的服務器。因此咱們來看看用這幾個框架構建一個服務器的時候有什麼異同。

 

3 建立一個服務器

全部開發者要開發Node.js web應用程序的第一步就是構建一個基本的服務器。因此咱們來看看用這幾個框架構建一個服務器的時候有什麼異同。

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 require進來,而後初始化一個實例而且賦值給一個爲app的變量。接下來這個實例初始化一個server監聽特定的端口,3000端口。app.listen()函數實際上包裝了node原生的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是很類似的。其實差異只是你把require那部分換成koa而不是express而已。app.listen()也是和Express如出一轍的對原生代碼的封裝函數。

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被require進來了可是沒有初始化一個hapi app而是構建了一個server而且指定了端口。在Express和Koa中咱們獲得的是一個回調函數而在hapi中咱們獲得的是一個新的server對象。一旦咱們調用了server.start()咱們就開啓了端口爲3000的服務器,而且返回一個回調函數。這個server.start()函數和Koa、Express不同,它並非一個http.CreateServer()的包裝函數,它的邏輯是由本身構建的。



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 /」請求而後調用一個回調函數,這個回調函數會被傳入reqres兩個對象。這個例子當中咱們只利用了resres.send()來返回整個頁面的字符串。Express有不少內置的方法能夠用來進行路由控制。getpostputheaddelete等等這些方法都是Express支持的最經常使用的方法(這只是一部分而已,並非所有)。

 

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。全部帶有*前綴的函數都表示這個函數會返回一個generator對象。根本上來講,generator會同步地yield出數據(譯註:若是對Python比較熟悉的話,應該對ES6的generator不陌生,這裏的yield其實和Python的yield語句差很少一個意思),這個超出本文所探索的內容,不詳述。在app.use()函數中,generator函數設置響應體。在Koa中,this這個上下文其實就是對node的requestresponse對象的封裝。this.body是KoaResponse對象的一個屬性。this.body能夠設置爲字符串, buffer, stream, 對象, 或者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方法能夠處理典型的例如GETPUTPOSTDELETE的請求,*通配符能夠匹配全部的路由。handler函數被傳入一個request對象的引用,它必須調用reply函數包含須要返回的數據。數據能夠是字符串、buffer、可序列化對象、或者stream。

 

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.Router()和不能用app.user('/api', router)之外,其實上是和Express 3.x本質上是同樣的。在Express 3.x中,你須要用app.route()替換router.route()而且須要加上/api前綴。Express 4.x的這種語法能夠減小開發者編碼錯誤而且你只須要修改少許代碼就能夠修改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這樣的能夠減小編碼重複路由規則的能力。它須要額外的中間件來處理路由控制。我選擇使用koa-route由於它是由Koa團隊維護的,可是還有不少由其餘開發者維護的可用的中間件。Koa的路由和Express同樣使用相似的關鍵詞來定義它們的方法,.get().put(),.post(), 和 .delete()。Koa在處理路由的時候有一個好處就是,它使用ES6的generators函數來減小對回調函數的處理。

 

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路由處理的第一印象就是,相對於其它兩個框架,這貨是多麼的清爽,可讀性是多麼的棒!即便是那些必須的methodpathhandlerreply配置參數都是那麼的賞心悅目(譯註:做者高潮了)。相似於Koa,Hapi不少重複的代碼會致使更大的出錯多可能性。然而這是Hapi的有意之爲,Hapi更關注配置而且但願使得代碼更加清晰和讓團隊開發使用起來更加簡便。Hapi但願能夠不須要開發者進行編碼的狀況下對錯誤處理進行優化。若是你嘗試去訪問一個沒有被定義的REST API,它會返回一個包含狀態碼和錯誤的描述的JSON對象。

 

5 優缺點比較

5.1 Express

5.1.1 優勢

Express擁有的社區不只僅是上面三者當中最大的,而且是全部Node.js web應用程序框架當中最大的。在通過其背後差很少5年的發展和在StrongLoop的掌管下,它是三者當中最成熟的框架。它爲服務器啓動和運行提供了簡單的方式,而且經過內置的路由提升了代碼的複用性。

5.1.2 缺點

使用Express須要手動處理不少單調乏味的任務。它沒有內置的錯誤處理。當你須要解決某個特定的問題的時候,你會容易迷失在衆多能夠添加的中間件中,在Express中,你有太多方式去解決同一個問題。Express自誇爲高度可配置,這有好處也有壞處,對於準備使用Express的剛入門的開發者來講,這不是一件好的事情。而且對比起其餘框架來講,Express體積更大。

5.2 Koa

5.2.1 優勢

Koa有着傲人的身材(體積小),它表現力更強;對比起其餘框架,它使得中間件的編寫變的更加容易。Koa基本上就是一個只有骨架的框架,你能夠選擇(或者本身寫一個)中間件,而不用妥協於Express或者Hapi它們自帶的中間件。它也是惟一一個採用ES6的框架,例如它使用了ES6的generators。

5.2.2 缺點

Koa不穩定,仍處於活躍的開發完善階段。使用ES6仍是有點太超前了,例如只有0.11.9+的Node.js版本才能運行Koa,而如今最新的Node.js穩定版本是0.10.33。和Express同樣有好也有壞的一點就是,在多種中間件的選擇仍是本身寫中間件。就像咱們以前所用的router那樣,有太多相似的router中間件可供咱們選擇。

5.3 Hapi

5.3.1 優勢

Hapi自豪地宣稱它本身是基於配置優於編碼的概念,而且不少開發者認爲這是一件好事。在團隊項目開發中,能夠很容易地加強一致性和可複用性。做爲有着大名鼎鼎的WalmartLabs支持的框架和其餘響噹噹的企業在實際生產中使用Hapi,它已經通過了實際戰場的洗禮,企業們能夠沒有擔心地基於Hopi運行本身的應用程序。全部的跡象都代表Hapi向着成爲的偉大的框架的方向持續成熟。

5.3.2 缺點

Hapi絕逼適合用來開發更大更復雜的應用。但對於一個簡單的web app來講,它的可能有點兒堆砌太多樣板代碼了。並且Hapi的可供參考樣例太少了,或者說開源的使用Hapi的應用程序太少了。因此選擇它對開發者的要求更高一點,而不是所使用的中間件。

 

6 總結

咱們已經看過三個框架一些棒棒的並且很實際的例子了。Express毫無疑問是三個當中最流行和最出名的框架。當你要開發一個新的應用程序的時候,使用Express來構建一個服務器可能已經成爲了你的條件反射了;但但願如今你在作選擇的時候會多一些思考,能夠考慮選擇Koa或者Hapi。Koa經過超前擁抱ES6和Web component的思想,顯示了Web開發社區正在進步的對將來的承諾。對於比較大的團隊和比較大的項目來講,Hapi應該成爲首要選擇。它所推崇的配置優於編碼,對團隊和對團隊一直追求的可複用性都大有裨益。如今趕忙行動起來嘗試使用一個新的框架,可能你會喜歡或者討厭它,但沒到最後你總不會知道結果是怎麼樣的,有一點無容置疑的是,它會讓你成爲一個更好的開發者。

相關文章
相關標籤/搜索