Node.js 是一個(gè)異步的世界,官方 API 支持的都是 callback 形式的異步編程模型,這會(huì)帶來(lái)許多問(wèn)題,例如
因此社區(qū)提供了各種異步的解決方案,最終勝出的是 Promise,它也內(nèi)置到了 ECMAScript 2015 中。而在 Promise 的基礎(chǔ)上,結(jié)合 Generator 提供的切換上下文能力,出現(xiàn)了 co 等第三方類庫(kù)來(lái)讓我們用同步寫(xiě)法編寫(xiě)異步代碼。同時(shí),async function 這個(gè)官方解決方案也于 ECMAScript 2017 中發(fā)布,并在 Node.js 8 中實(shí)現(xiàn)。
async function 是語(yǔ)言層面提供的語(yǔ)法糖,在 async function 中,我們可以通過(guò) await 關(guān)鍵字來(lái)等待一個(gè) Promise 被 resolve(或者 reject,此時(shí)會(huì)拋出異常), Node.js 現(xiàn)在的 LTS 版本(8.x)已原生支持。
const fn = async function() { |
Koa 是一個(gè)新的 web 框架,由 Express 幕后的原班人馬打造, 致力于成為 web 應(yīng)用和 API 開(kāi)發(fā)領(lǐng)域中的一個(gè)更小、更富有表現(xiàn)力、更健壯的基石。
Koa 和 Express 的設(shè)計(jì)風(fēng)格非常類似,底層也都是共用的同一套 HTTP 基礎(chǔ)庫(kù),但是有幾個(gè)顯著的區(qū)別,除了上面提到的默認(rèn)異步解決方案之外,主要的特點(diǎn)還有下面幾個(gè)。
Koa 的中間件和 Express 不同,Koa 選擇了洋蔥圈模型。
所有的請(qǐng)求經(jīng)過(guò)一個(gè)中間件的時(shí)候都會(huì)執(zhí)行兩次,對(duì)比 Express 形式的中間件,Koa 的模型可以非常方便的實(shí)現(xiàn)后置處理邏輯,對(duì)比 Koa 和 Express 的 Compress 中間件就可以明顯的感受到 Koa 中間件模型的優(yōu)勢(shì)。
和 Express 只有 Request 和 Response 兩個(gè)對(duì)象不同,Koa 增加了一個(gè) Context 的對(duì)象,作為這次請(qǐng)求的上下文對(duì)象(在 Koa 1 中為中間件的 this,在 Koa 2 中作為中間件的第一個(gè)參數(shù)傳入)。我們可以將一次請(qǐng)求相關(guān)的上下文都掛載到這個(gè)對(duì)象上。類似 traceId 這種需要貫穿整個(gè)請(qǐng)求(在后續(xù)任何一個(gè)地方進(jìn)行其他調(diào)用都需要用到)的屬性就可以掛載上去。相較于 request 和 response 而言更加符合語(yǔ)義。
同時(shí) Context 上也掛載了 Request 和 Response 兩個(gè)對(duì)象。和 Express 類似,這兩個(gè)對(duì)象都提供了大量的便捷方法輔助開(kāi)發(fā),例如
通過(guò)同步方式編寫(xiě)異步代碼帶來(lái)的另外一個(gè)非常大的好處就是異常處理非常自然,使用 try catch 就可以將按照規(guī)范編寫(xiě)的代碼中的所有錯(cuò)誤都捕獲到。這樣我們可以很便捷的編寫(xiě)一個(gè)自定義的錯(cuò)誤處理中間件。
async function onerror(ctx, next) { |
只需要將這個(gè)中間件放在其他中間件之前,就可以捕獲它們所有的同步或者異步代碼中拋出的異常了。
如上述,Koa 是一個(gè)非常優(yōu)秀的框架,然而對(duì)于企業(yè)級(jí)應(yīng)用來(lái)說(shuō),它還比較基礎(chǔ)。
而 Egg 選擇了 Koa 作為其基礎(chǔ)框架,在它的模型基礎(chǔ)上,進(jìn)一步對(duì)它進(jìn)行了一些增強(qiáng)。
在基于 Egg 的框架或者應(yīng)用中,我們可以通過(guò)定義 app/extend/{application,context,request,response}.js 來(lái)擴(kuò)展 Koa 中對(duì)應(yīng)的四個(gè)對(duì)象的原型,通過(guò)這個(gè)功能,我們可以快速的增加更多的輔助方法,例如我們?cè)?nbsp;app/extend/context.js 中寫(xiě)入下列代碼:
// app/extend/context.js |
在 Controller 中,我們就可以使用到剛才定義的這個(gè)便捷屬性了:
// app/controller/home.js |
更多關(guān)于擴(kuò)展的內(nèi)容,請(qǐng)查看擴(kuò)展章節(jié)。
眾所周知,在 Express 和 Koa 中,經(jīng)常會(huì)引入許許多多的中間件來(lái)提供各種各樣的功能,例如引入 koa-session 提供 Session 的支持,引入 koa-bodyparser 來(lái)解析請(qǐng)求 body。而 Egg 提供了一個(gè)更加強(qiáng)大的插件機(jī)制,讓這些獨(dú)立領(lǐng)域的功能模塊可以更加容易編寫(xiě)。
一個(gè)插件可以包含
一個(gè)獨(dú)立領(lǐng)域下的插件實(shí)現(xiàn),可以在代碼維護(hù)性非常高的情況下實(shí)現(xiàn)非常完善的功能,而插件也支持配置各個(gè)環(huán)境下的默認(rèn)(最佳)配置,讓我們使用插件的時(shí)候幾乎可以不需要修改配置項(xiàng)。
egg-security 插件就是一個(gè)典型的例子。
更多關(guān)于插件的內(nèi)容,請(qǐng)查看插件章節(jié)。
Egg 1.x 發(fā)布時(shí),Node.js 的 LTS 版本尚不支持 async function,所以 Egg 1.x 仍然基于 Koa 1.x 開(kāi)發(fā),但是在此基礎(chǔ)上,Egg 全面增加了 async function 的支持,再加上 Egg 對(duì) Koa 2.x 的中間件也完全兼容,應(yīng)用層代碼可以完全基于 async function 來(lái)開(kāi)發(fā)。
Node.js 8 正式進(jìn)入 LTS 后,async function 可以在 Node.js 中使用并且沒(méi)有任何性能問(wèn)題了,Egg 2.x 基于 Koa 2.x,框架底層以及所有內(nèi)置插件都使用 async function 編寫(xiě),并保持了對(duì) Egg 1.x 以及 generator function 的完全兼容,應(yīng)用層只需要升級(jí)到 Node.js 8 即可從 Egg 1.x 遷移到 Egg 2.x。
更多建議: