一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Node.js框架比較: Express vs. Koa vs. Hapi

 念念爸 2016-06-23

1、介紹

Express.js無(wú)疑是當(dāng)前Node.js中最流行的Web應(yīng)用程序框架。它幾乎成為了大多數(shù)Node.js web應(yīng)用程序的基本的依賴(lài),甚至一些例如Sails.js這樣的流行的框架也是基于Express.js。然而你還有一些其他框架的選擇,可以給你帶來(lái)“sinatra”一樣的感覺(jué)(譯注:sinatra是一個(gè)簡(jiǎn)單的Ruby的Web框架,可以參考這篇博文)。另外兩個(gè)最流行的框架分別是Koa和Hapi。

這篇文章不是打算說(shuō)服你哪個(gè)框架比另外一個(gè)更好,而是只是打算讓你更好地理解每個(gè)框架能做什么,什么情況下一個(gè)框架可以秒殺另外一個(gè)。

2、框架的背景

我們將要探討的兩個(gè)框架看起來(lái)都非常相似。每一個(gè)都能夠用幾行代碼來(lái)構(gòu)建一個(gè)服務(wù)器,并都可以非常輕易地構(gòu)建REST API。我們先瞧瞧這幾個(gè)框架是怎么誕生的。

2.1 Express

2009年6月26日,TJ Holowaychuk提交了Express的第一次commit,接下來(lái)在2010年1月2日,有660次commits的Express 0.0.1版本正式發(fā)布。TJ和Ciaron Jessup是當(dāng)時(shí)最主要的兩個(gè)代碼貢獻(xiàn)者。在第一個(gè)版本發(fā)布的時(shí)候,根據(jù)github上的readme.md,這個(gè)框架被描述成:

瘋一般快速(而簡(jiǎn)潔)的服務(wù)端JavaScript Web開(kāi)發(fā)框架,基于Node.js和V8 JavaScript引擎。

差不多5年的時(shí)間過(guò)去了,Express擁有了4,925次commit,現(xiàn)在Express的最新版本是4.10.1,由StrongLoop維護(hù),因?yàn)門(mén)J現(xiàn)在已經(jīng)跑去玩Go了。

2.2 Koa

大概在差不多一年前的2013年8月17日,TJ Holowaychuk(又是他!)只身一人提交了Koa的第一次commit。他描述Koa為“表現(xiàn)力強(qiáng)勁的Node.js中間件,通過(guò)co使用generators使得編寫(xiě)web應(yīng)用程序和REST API更加絲般順滑”。Koa被標(biāo)榜為只占用約400行源碼空間的框架。Koa的目前最新版本為0.13.0,擁有583次commits。

2.3 Hapi

2011年8月5日,WalmartLabs的一位成員Eran Hammer提交了Hapi的第一次commit。Hapi原本是Postmile的一部分,并且最開(kāi)始是基于Express構(gòu)建的。后來(lái)它發(fā)展成自己自己的框架,正如Eran在他的博客里面所說(shuō)的:

Hapi基于這么一個(gè)想法:配置優(yōu)于編碼,業(yè)務(wù)邏輯必須和傳輸層進(jìn)行分離..

Hapi最新版本為7.2.0,擁有3,816次commits,并且仍然由Eran Hammer維護(hù)。

所有開(kāi)發(fā)者要開(kāi)發(fā)Node.js web應(yīng)用程序的第一步就是構(gòu)建一個(gè)基本的服務(wù)器。所以我們來(lái)看看用這幾個(gè)框架構(gòu)建一個(gè)服務(wù)器的時(shí)候有什么異同。

3 創(chuàng)建一個(gè)服務(wù)器

所有開(kāi)發(fā)者要開(kāi)發(fā)Node.js web應(yīng)用程序的第一步就是構(gòu)建一個(gè)基本的服務(wù)器。所以我們來(lái)看看用這幾個(gè)框架構(gòu)建一個(gè)服務(wù)器的時(shí)候有什么異同。

3.1 Express

  1. var express = require(express);
  2. var app = express();
  3.  
  4. var server = app.listen(3000, function() { 
  5.     console.log(Express is listening to http://localhost:3000);
  6. });

對(duì)于所有的node開(kāi)發(fā)者來(lái)說(shuō),這看起來(lái)相當(dāng)?shù)淖匀弧N覀儼裡xpress require進(jìn)來(lái),然后初始化一個(gè)實(shí)例并且賦值給一個(gè)為app的變量。接下來(lái)這個(gè)實(shí)例初始化一個(gè)server監(jiān)聽(tīng)特定的端口,3000端口。app.listen()函數(shù)實(shí)際上包裝了node原生的http.createServer()函數(shù)。

3.2 Koa

  1. var koa = require(koa);
  2. var app = koa();
  3.  
  4. var server = app.listen(3000, function() {
  5.     console.log(Koa is listening to http://localhost:3000);
  6. });

你馬上發(fā)現(xiàn)Koa和Express是很相似的。其實(shí)差別只是你把require那部分換成koa而不是express而已。app.listen()也是和Express一模一樣的對(duì)原生代碼的封裝函數(shù)。

3.3 Hapi

  1. var Hapi = require(hapi);
  2. var server = new Hapi.Server(3000);
  3.  
  4. server.start(function() {
  5.     console.log(Hapi is listening to http://localhost:3000);
  6. });

Hapi是三者中最獨(dú)特的一個(gè)。和其他兩者一樣,hapi被require進(jìn)來(lái)了但是沒(méi)有初始化一個(gè)hapi app而是構(gòu)建了一個(gè)server并且指定了端口。在Express和Koa中我們得到的是一個(gè)回調(diào)函數(shù)而在hapi中我們得到的是一個(gè)新的server對(duì)象。一旦我們調(diào)用了server.start()我們就開(kāi)啟了端口為3000的服務(wù)器,并且返回一個(gè)回調(diào)函數(shù)。這個(gè)server.start()函數(shù)和Koa、Express不一樣,它并不是一個(gè)http.CreateServer()的包裝函數(shù),它的邏輯是由自己構(gòu)建的。

4 路由控制

現(xiàn)在一起來(lái)搞搞一下服務(wù)器最重要的特定之一,路由控制。我們先用每個(gè)框架分別構(gòu)建一個(gè)老掉渣的“Hello world”應(yīng)用程序,然后我們?cè)偬剿饕幌乱恍└杏玫臇|東,REST API。

4.1 Hello world

4.1.1 Express

  1. var express = require(express);
  2. var app = express();
  3.  
  4. app.get(/, function(req, res) {
  5.     res.send(Hello world);
  6. });
  7.  
  8. var server = app.listen(3000, function() {
  9.     console.log(Express is listening to http://localhost:3000);
  10. });

我們用get()函數(shù)來(lái)捕獲“GET /”請(qǐng)求然后調(diào)用一個(gè)回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)會(huì)被傳入req和res兩個(gè)對(duì)象。這個(gè)例子當(dāng)中我們只利用了res的res.send()來(lái)返回整個(gè)頁(yè)面的字符串。Express有很多內(nèi)置的方法可以用來(lái)進(jìn)行路由控制。get, post, put, head, delete等等這些方法都是Express支持的最常用的方法(這只是一部分而已,并不是全部)。

4.1.2 Koa

  1. var koa = require(koa);
  2. var app = koa();
  3.  
  4. app.use(function *() {
  5.     this.body = Hello world;
  6. });
  7.  
  8. var server = app.listen(3000, function() {
  9.     console.log(Koa is listening to http://localhost:3000);
  10. });

Koa和Express稍微有點(diǎn)兒不同,它用了ES6的generators。所有帶有*前綴的函數(shù)都表示這個(gè)函數(shù)會(huì)返回一個(gè)generator對(duì)象。根本上來(lái)說(shuō),generator會(huì)同步地yield出數(shù)據(jù)(譯注:如果對(duì)Python比較熟悉的話(huà),應(yīng)該對(duì)ES6的generator不陌生,這里的yield其實(shí)和Python的yield語(yǔ)句差不多一個(gè)意思),這個(gè)超出本文所探索的內(nèi)容,不詳述。在app.use()函數(shù)中,generator函數(shù)設(shè)置響應(yīng)體。在Koa中,this這個(gè)上下文其實(shí)就是對(duì)node的request和response對(duì)象的封裝。this.body是KoaResponse對(duì)象的一個(gè)屬性。this.body可以設(shè)置為字符串, buffer, stream, 對(duì)象, 或者null也行。上面的例子中我們使用了Koa為數(shù)不多的中間件的其中一個(gè)。這個(gè)中間件捕獲了所有的路由并且響應(yīng)同一個(gè)字符串。

4.1.3 Hapi

  1. var Hapi = require(hapi);
  2. var server = new Hapi.Server(3000);
  3.  
  4. server.route({
  5.     method: GET,
  6.     path: /,
  7.     handler: function(request, reply) {
  8.         reply(Hello world);
  9.     }
  10. });
  11.  
  12. server.start(function() {
  13.     console.log(Hapi is listening to http://localhost:3000);
  14. });

這里使用了server對(duì)象給我們提供的server.route內(nèi)置的方法,這個(gè)方法接受配置參數(shù):path(必須),method(必須),vhost,和handler(必須)。HTTP方法可以處理典型的例如GET、PUT、POST、DELETE的請(qǐng)求,*通配符可以匹配所有的路由。handler函數(shù)被傳入一個(gè)request對(duì)象的引用,它必須調(diào)用reply函數(shù)包含需要返回的數(shù)據(jù)。數(shù)據(jù)可以是字符串、buffer、可序列化對(duì)象、或者stream。

4.2 REST API

Hello world除了給我們展示了如何讓一個(gè)應(yīng)用程序運(yùn)行起來(lái)以外幾乎啥都沒(méi)干。在所有的重?cái)?shù)據(jù)的應(yīng)用程序當(dāng)中,REST API幾乎是一個(gè)必須的設(shè)計(jì),并且能讓我們更好地理解這些框架是可以如何使用的?,F(xiàn)在讓我們看看這些框架是怎么處理REST API的。

4.2.1 Express

  1. var express = require(express);
  2. var app = express();
  3. var router = express.Router();
  4.  
  5. // REST API
  6. router.route(/items)
  7. .get(function(req, res, next) {
  8.   res.send(Get);
  9. })
  10. .post(function(req, res, next) {
  11.   res.send(Post);
  12. });
  13.  
  14. router.route(/items/:id)
  15. .get(function(req, res, next) {
  16.   res.send(Get id: + req.params.id);
  17. })
  18. .put(function(req, res, next) {
  19.   res.send(Put id: + req.params.id);
  20. })
  21. .delete(function(req, res, next) {
  22.   res.send(Delete id: + req.params.id);
  23. });
  24.  
  25. app.use(/api, router);
  26.  
  27. // index
  28. app.get(/, function(req, res) {
  29.   res.send(Hello world);
  30. });
  31.  
  32. var server = app.listen(3000, function() {
  33.   console.log(Express is listening to http://localhost:3000);
  34. });

4.2.1 Express

  1. var express = require(express);
  2. var app = express();
  3. var router = express.Router();
  4.  
  5. // REST API
  6. router.route(/items)
  7. .get(function(req, res, next) {
  8.   res.send(Get);
  9. })
  10. .post(function(req, res, next) {
  11.   res.send(Post);
  12. });
  13.  
  14. router.route(/items/:id)
  15. .get(function(req, res, next) {
  16.   res.send(Get id: + req.params.id);
  17. })
  18. .put(function(req, res, next) {
  19.   res.send(Put id: + req.params.id);
  20. })
  21. .delete(function(req, res, next) {
  22.   res.send(Delete id: + req.params.id);
  23. });
  24.  
  25. app.use(/api, router);
  26.  
  27. // index
  28. app.get(/, function(req, res) {
  29.   res.send(Hello world);
  30. });
  31.  
  32. var server = app.listen(3000, function() {
  33.   console.log(Express is listening to http://localhost:3000);
  34. });

我們?yōu)橐延械腍ello World應(yīng)用程序添加REST API。Express提供一些處理路由的便捷的方式。這是Express 4.x的語(yǔ)法,除了你不需要express.Router()和不能用app.user(‘/api’, router)以外,其實(shí)上是和Express 3.x本質(zhì)上是一樣的。在Express 3.x中,你需要用app.route()替換router.route()并且需要加上/api前綴。Express 4.x的這種語(yǔ)法可以減少開(kāi)發(fā)者編碼錯(cuò)誤并且你只需要修改少量代碼就可以修改HTTP方法規(guī)則。

4.2.2 Koa

  1. var koa = require(koa);
  2. var route = require(koa-route);
  3. var app = koa();
  4.  
  5. // REST API
  6. app.use(route.get(/api/items, function*() {
  7.     this.body = Get;
  8. }));
  9. app.use(route.get(/api/items/:id, function*(id) {
  10.     this.body = Get id: + id;
  11. }));
  12. app.use(route.post(/api/items, function*() {
  13.     this.body = Post;
  14. }));
  15. app.use(route.put(/api/items/:id, function*(id) {
  16.     this.body = Put id: + id;
  17. }));
  18. app.use(route.delete(/api/items/:id, function*(id) {
  19.     this.body = Delete id: + id;
  20. }));
  21.  
  22. // all other routes
  23. app.use(function *() {
  24.     this.body = Hello world;
  25. });
  26.  
  27. var server = app.listen(3000, function() {
  28.   console.log(Koa is listening to http://localhost:3000);
  29. });

很明顯,Koa并沒(méi)有類(lèi)似Express這樣的可以減少編碼重復(fù)路由規(guī)則的能力。它需要額外的中間件來(lái)處理路由控制。我選擇使用koa-route因?yàn)樗怯蒏oa團(tuán)隊(duì)維護(hù)的,但是還有很多由其他開(kāi)發(fā)者維護(hù)的可用的中間件。Koa的路由和Express一樣使用類(lèi)似的關(guān)鍵詞來(lái)定義它們的方法,.get(), .put(),.post(), 和 .delete()。Koa在處理路由的時(shí)候有一個(gè)好處就是,它使用ES6的generators函數(shù)來(lái)減少對(duì)回調(diào)函數(shù)的處理。

4.2.3 Hapi

  1. var Hapi = require(hapi);
  2. var server = new Hapi.Server(3000);
  3.  
  4. server.route([
  5.   {
  6.     method: 'GET',
  7.     path: '/api/items',
  8.     handler: function(request, reply) {
  9.       reply('Get item id');
  10.     }
  11.   },
  12.   {
  13.     method: 'GET',
  14.     path: '/api/items/{id}',
  15.     handler: function(request, reply) {
  16.       reply('Get item id: ' + request.params.id);
  17.     }
  18.   },
  19.   {
  20.     method: 'POST',
  21.     path: '/api/items',
  22.     handler: function(request, reply) {
  23.       reply('Post item');
  24.     }
  25.   },
  26.   {
  27.     method: 'PUT',
  28.     path: '/api/items/{id}',
  29.     handler: function(request, reply) {
  30.       reply('Put item id: ' + request.params.id);
  31.     }
  32.   },
  33.   {
  34.     method: 'DELETE',
  35.     path: '/api/items/{id}',
  36.     handler: function(request, reply) {
  37.       reply('Delete item id: ' + request.params.id);
  38.     }
  39.   },
  40.   {
  41.     method: 'GET',
  42.     path: '/',
  43.     handler: function(request, reply) {
  44.       reply('Hello world');
  45.     }
  46.   }
  47. ]);
  48.  
  49. server.start(function() {
  50.   console.log(Hapi is listening to http://localhost:3000);
  51. });

對(duì)于Hapi路由處理的第一印象就是,相對(duì)于其它兩個(gè)框架,這貨是多么的清爽,可讀性是多么的棒!即使是那些必須的method,path,handler和reply配置參數(shù)都是那么的賞心悅目(譯注:作者高潮了)。類(lèi)似于Koa,Hapi很多重復(fù)的代碼會(huì)導(dǎo)致更大的出錯(cuò)多可能性。然而這是Hapi的有意之為,Hapi更關(guān)注配置并且希望使得代碼更加清晰和讓團(tuán)隊(duì)開(kāi)發(fā)使用起來(lái)更加簡(jiǎn)便。Hapi希望可以不需要開(kāi)發(fā)者進(jìn)行編碼的情況下對(duì)錯(cuò)誤處理進(jìn)行優(yōu)化。如果你嘗試去訪問(wèn)一個(gè)沒(méi)有被定義的REST API,它會(huì)返回一個(gè)包含狀態(tài)碼和錯(cuò)誤的描述的JSON對(duì)象。

5 優(yōu)缺點(diǎn)比較

5.1 Express

5.1.1 優(yōu)點(diǎn)

Express擁有的社區(qū)不僅僅是上面三者當(dāng)中最大的,并且是所有Node.js web應(yīng)用程序框架當(dāng)中最大的。在經(jīng)過(guò)其背后差不多5年的發(fā)展和在StrongLoop的掌管下,它是三者當(dāng)中最成熟的框架。它為服務(wù)器啟動(dòng)和運(yùn)行提供了簡(jiǎn)單的方式,并且通過(guò)內(nèi)置的路由提高了代碼的復(fù)用性。

5.1.2 缺點(diǎn)

使用Express需要手動(dòng)處理很多單調(diào)乏味的任務(wù)。它沒(méi)有內(nèi)置的錯(cuò)誤處理。當(dāng)你需要解決某個(gè)特定的問(wèn)題的時(shí)候,你會(huì)容易迷失在眾多可以添加的中間件中,在Express中,你有太多方式去解決同一個(gè)問(wèn)題。Express自詡為高度可配置,這有好處也有壞處,對(duì)于準(zhǔn)備使用Express的剛?cè)腴T(mén)的開(kāi)發(fā)者來(lái)說(shuō),這不是一件好的事情。并且對(duì)比起其他框架來(lái)說(shuō),Express體積更大。

5.2 Koa

5.2.1 優(yōu)點(diǎn)

Koa有著傲人的身材(體積小),它表現(xiàn)力更強(qiáng);對(duì)比起其他框架,它使得中間件的編寫(xiě)變的更加容易。Koa基本上就是一個(gè)只有骨架的框架,你可以選擇(或者自己寫(xiě)一個(gè))中間件,而不用妥協(xié)于Express或者Hapi它們自帶的中間件。它也是唯一一個(gè)采用ES6的框架,例如它使用了ES6的generators。

5.2.2 缺點(diǎn)

Koa不穩(wěn)定,仍處于活躍的開(kāi)發(fā)完善階段。使用ES6還是有點(diǎn)太超前了,例如只有0.11.9+的Node.js版本才能運(yùn)行Koa,而現(xiàn)在最新的Node.js穩(wěn)定版本是0.10.33。和Express一樣有好也有壞的一點(diǎn)就是,在多種中間件的選擇還是自己寫(xiě)中間件。就像我們之前所用的router那樣,有太多類(lèi)似的router中間件可供我們選擇。

5.3 Hapi

5.3.1 優(yōu)點(diǎn)

Hapi自豪地宣稱(chēng)它自己是基于配置優(yōu)于編碼的概念,并且很多開(kāi)發(fā)者認(rèn)為這是一件好事。在團(tuán)隊(duì)項(xiàng)目開(kāi)發(fā)中,可以很容易地增強(qiáng)一致性和可復(fù)用性。作為有著大名鼎鼎的WalmartLabs支持的框架和其他響當(dāng)當(dāng)?shù)钠髽I(yè)在實(shí)際生產(chǎn)中使用Hapi,它已經(jīng)經(jīng)過(guò)了實(shí)際戰(zhàn)場(chǎng)的洗禮,企業(yè)們可以沒(méi)有擔(dān)憂(yōu)地基于Hopi運(yùn)行自己的應(yīng)用程序。所有的跡象都表明Hapi向著成為的偉大的框架的方向持續(xù)成熟。

5.3.2 缺點(diǎn)

Hapi絕逼適合用來(lái)開(kāi)發(fā)更大更復(fù)雜的應(yīng)用。但對(duì)于一個(gè)簡(jiǎn)單的web app來(lái)說(shuō),它的可能有點(diǎn)兒堆砌太多樣板代碼了。而且Hapi的可供參考樣例太少了,或者說(shuō)開(kāi)源的使用Hapi的應(yīng)用程序太少了。所以選擇它對(duì)開(kāi)發(fā)者的要求更高一點(diǎn),而不是所使用的中間件。

6 總結(jié)

我們已經(jīng)看過(guò)三個(gè)框架一些棒棒的而且很實(shí)際的例子了。Express毫無(wú)疑問(wèn)是三個(gè)當(dāng)中最流行和最出名的框架。當(dāng)你要開(kāi)發(fā)一個(gè)新的應(yīng)用程序的時(shí)候,使用Express來(lái)構(gòu)建一個(gè)服務(wù)器可能已經(jīng)成為了你的條件反射了;但希望現(xiàn)在你在做選擇的時(shí)候會(huì)多一些思考,可以考慮選擇Koa或者Hapi。Koa通過(guò)超前擁抱ES6和Web component的思想,顯示了Web開(kāi)發(fā)社區(qū)正在進(jìn)步的對(duì)未來(lái)的承諾。對(duì)于比較大的團(tuán)隊(duì)和比較大的項(xiàng)目來(lái)說(shuō),Hapi應(yīng)該成為首要選擇。它所推崇的配置優(yōu)于編碼,對(duì)團(tuán)隊(duì)和對(duì)團(tuán)隊(duì)一直追求的可復(fù)用性都大有裨益。現(xiàn)在趕緊行動(dòng)起來(lái)嘗試使用一個(gè)新的框架,可能你會(huì)喜歡或者討厭它,但沒(méi)到最后你總不會(huì)知道結(jié)果是怎么樣的,有一點(diǎn)無(wú)容置疑的是,它會(huì)讓你成為一個(gè)更好的開(kāi)發(fā)者。

英文原文:Node.js Framework Comparison: Express vs. Koa vs. Hapi
作者:Jonathan
譯者:戴嘉華
譯文:戴嘉華博客, segmentfault

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多

    精品少妇一区二区视频| 91欧美亚洲精品在线观看| 日本一本不卡免费视频| 一区中文字幕人妻少妇| 国产高清精品福利私拍| 黑人粗大一区二区三区| 久久本道综合色狠狠五月| 国产成人精品午夜福利| 亚洲精品伦理熟女国产一区二区| 91精品国产品国语在线不卡| 人人爽夜夜爽夜夜爽精品视频| 国产欧美日产久久婷婷| 亚洲欧美一二区日韩高清在线| 国产对白老熟女正在播放| 色丁香之五月婷婷开心| 欧美一区日韩二区亚洲三区| 五月天丁香婷婷一区二区| 丰满人妻一二三区av| 久久精品a毛片看国产成人| 美女露小粉嫩91精品久久久| 日韩一区二区三区在线日| 欧美字幕一区二区三区| 国产av乱了乱了一区二区三区| 人妻中文一区二区三区| 中文字幕精品一区二区年下载| 久久久精品日韩欧美丰满| 国产主播精品福利午夜二区| 亚洲综合色婷婷七月丁香| 国产麻豆精品福利在线| 亚洲另类女同一二三区| 国产成人精品在线播放| 日韩人妻少妇一区二区| 亚洲免费视频中文字幕在线观看| 国产成人高清精品尤物| 不卡视频在线一区二区三区| 色好吊视频这里只有精| 午夜久久精品福利视频| 99久久精品午夜一区二区| 黑鬼糟蹋少妇资源在线观看| 国产色一区二区三区精品视频| 九九热这里只有精品哦|