本文轉自:http://www.javashuo.com/article/p-vduicbel-eo.htmlhtml
本文翻譯自: 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
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
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
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的開發者第一步操做就是建立一個服務。由於下面咱們將依次使用每一個框架來分別建立一個服務,來看看他們之間的類似處與不一樣的地方。
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()進行了一層封裝。
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()進行了一層封裝。
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。
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…
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核心庫提供了不少中間件,這裏咱們只是使用了其中的一個,這個中間件能夠捕獲全部的路由,而後響應一個字符串。
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是必須被執行的方法,並且須要傳入一項數據,這個數據能夠是字符串、序列化的對象或者流。
Hello World程序歷來都沒有太多的指望,由於它只能展現建立及運行一個應用最基本最簡單的操做。REST API幾乎是全部大型應用程序所必須的一個功能,同時對於咱們更好的理解這些框架有很大的幫助。所以接下來咱們將看看這幾個框架是如何來處理REST API。
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方法進行了最小的一個修改。
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函數,從而下降了回調函數的複雜度。
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對象。
Express擁有最大社區,比僅僅是跟這三個框架相比,而是對於全部的Nodejs框架來講也是最大的。目前來講,他是最爲三者中最爲成熟的框架,接近5年的開發投入,同時還採用了StrongLoop(注:StrongLoop是一個進程管理工具,提供CLI與UI界面。)對線上倉庫的代碼進行管理。他提供了一種簡單的方式來建立和運行一個服務,同時路由的內置也使得代碼獲得了重複使用。
在使用Express過程當中,咱們每每要處理不少單調乏味的任務。好比他沒有內置的錯誤處理機制,另外對於一樣一個問題能夠有不少中間件來供選擇,這也使得開發者容易迷失在中間件的選擇中,總而言之就是,一個問題你會有N多解決方案。Express聲稱本身是可配置選擇的,這其實不沒有好或很差,可是對於一個剛剛接觸Express的開發者來講,這就是他的劣勢了。另外,Express跟其餘的框架相比也還有很大的差距。
Koa的一個小進步就是他的代碼比較富有表現力,開發中間件也比其餘框架更容易得多。Koa是一個很基礎的準系統框架,開發者能夠選擇(或開發)他們所須要的中間件,而不是去選擇Express或Hapi的中間件。他同時也是三者中惟一一個積極擁抱ES6的框架,好比採用了ES6 generators函數。
目前Koa還處於不穩定版本,還處在開發階段。使用ES6進行開發的確是處於領先水平,好比Koa須要基於Nodejs 0.11.9以上的版本運行,而目前nodejs的文本版本是0.10.33。這是一件能夠算做好也能夠算做很差的事情,就像Express開發者有不少中間件要選擇甚至本身開發中間件同樣。好比咱們在上面看到的同樣,對於路由來講就有不少中間件供咱們選擇。
Hapi一直很自豪的說他們的框架是配置優於代碼,固然也有不少開發者可能會質疑把這一點算做是優點。但這一點對於大型項目組來講,的確是能夠保持代碼的統一性以及代碼複用性。另外這款框架是由沃爾瑪實驗室支持的,也有不少大公司在線上環境使用Hapi,代表他已經經過了嚴峻的測試,由於這些公司會考慮得更多才會使用Hapi來運行他們的項目。所以全部的這些跡象都代表Hapi正在朝一個偉大的框架發展。
Hapi的定位更傾向於大型或複雜的應用程序。對於一個簡單的應用來講,Hapi在代碼上反而有些顯得冗餘了,另外目前Hapi所提供的樣例程序也比較少,使用Hapi進行開發的開源應用一樣不多。所以,若是選擇Hapi的話,你可能要投入更多精力進行開發,而不是簡單的調用一個第三方中間件。
咱們已經看了三個框架還算不錯具備表明性的一些樣例代碼。Express仍然是當下最爲流行,以及最被人所知曉的框架。當開始一個新的開發項目時,可能你們的第一反應就是用Express來建立一個服務。可是如今更但願你們多考慮考慮使用Koa或者Hapi。Koa積極擁抱ES6的語法,展現了promise的真正魅力。目前整個web開發社區也都意識到ES6的優點,正在逐步往上面遷移。Hapi應該是大型項目組或者大型項目的第一選擇。他所倡導的配置優於代碼會使得項目組 在代碼的重複性上受益匪淺,這也正是大多數項目組所追求的目標。如今行動起來,嘗試一款新的框架吧,可能你會喜歡他也可能會討厭他,但若是不去嘗試你永遠也不會知道結果是什麼,最終全部的這些經歷都會讓你成長爲一個更加優秀的開發者。