node.js中koa和express的差異對(duì)比
前言
最近利用業(yè)余的時(shí)間,跟著 coderwhy 老師學(xué)習(xí) node.js,了解以及掌握一些服務(wù)端的常見知識(shí):
fileSystem:文件讀取模塊。events:事件流Buffer:node 中處理二進(jìn)制的方式http 創(chuàng)建服務(wù)器Stream流的讀寫操作…
確實(shí)學(xué)習(xí)到了很多東西,填充了自己的知識(shí)體系的未知領(lǐng)域。
node.js 也許是前端開發(fā)者踏入服務(wù)端開發(fā)的最好選擇。同樣的 JavaScript,同樣的語法,以及同樣的你,基本上可以達(dá)到無縫銜接的開發(fā)。
對(duì)于 node.js 而言,社區(qū)里面出現(xiàn)了非常多的框架,快速上手,敏捷開發(fā)。
koa 和 express 就是其中的比較兩個(gè)突出的框架。
在閱讀下文之前,希望你掌握了 express 和 koa 的基本使用,不然下面內(nèi)容對(duì)你的幫助也許不是那么的大。
koa 和 express 的介紹
express 是一個(gè)基于 node.js 平臺(tái),快速,開放,極簡的 web 開發(fā)框架。express 官網(wǎng)
koa 是基于 node.js 平臺(tái)的下一代 web 開發(fā)框架。koa 官網(wǎng)
兩個(gè)框架都是同一個(gè)團(tuán)隊(duì)開發(fā)的,都是 node.js 中比較優(yōu)秀的框架。
都是 node.js 框架,同一個(gè)團(tuán)隊(duì)為什么要開發(fā)兩個(gè)呢?
express 也許是 node.js 最早出的框架,里面集成了大量的中間件,造成了框架的笨重性。團(tuán)隊(duì)領(lǐng)隊(duì)人 TJ 發(fā)現(xiàn)了 express 的設(shè)計(jì)是有缺陷的,如果想要修復(fù)這個(gè)設(shè)計(jì)缺陷,就會(huì)造成 express 的框架重構(gòu)。
基于上面的種種原因(當(dāng)然還有不知道的),就打算重新寫一個(gè)新的框架->koa,來實(shí)現(xiàn) express 的不足之處。
express 現(xiàn)在有團(tuán)隊(duì)中的團(tuán)員維護(hù),基本上也很少更新了。
koa 由團(tuán)隊(duì)領(lǐng)隊(duì)人 TJ 主要維護(hù)。
? --來自 coderwhy 老師的閑談
上面的閑談的內(nèi)容不重要,當(dāng)個(gè)樂子開心一下就好了。
一起看看 express 和 koa 在 github 上的星標(biāo):

可以發(fā)現(xiàn) express 的使用率還是比 koa 高,盡管 express 笨重,設(shè)計(jì)有缺陷,但是對(duì)于開發(fā)者而言,當(dāng)不考慮這些因素情況下,express 還是吃香的。
兩者都是同一團(tuán)隊(duì)寫的兩個(gè)框架,那么核心的思想肯定是相同的,當(dāng)然肯定會(huì)也存在差異,那么接下來就來重點(diǎn)比較一下其中的差異吧。
koa 和 express 的差異對(duì)比
下面主要從兩個(gè)方面來分析其中的差異:設(shè)計(jì)架構(gòu)、中間件。
因?yàn)橐彩堑谝淮谓佑| koa (express 以前是接觸了的),如果存在有誤的地方,請(qǐng)指出來,虛心受教,共同進(jìn)步。
koa 和 express 的設(shè)計(jì)架構(gòu)對(duì)比
express 是完整和強(qiáng)大的,里面集成了大量的中間件,比如說:路由,靜態(tài)資源等中間件。對(duì)于開發(fā)者而言,技術(shù)統(tǒng)一,都是使用 express 內(nèi)部提供的。

koa 是靈活和自由的,基本上沒有集成任何中間件(核心代碼只有大致1600行左右),中間件都需要自己去安裝。對(duì)于開發(fā)者而言,選擇技術(shù)的類型是多樣的,豐富的。

webstorm 和 vscode 的使用,都是因人而異,沒有誰強(qiáng)誰弱。那么對(duì)于 express 和 koa 也是同樣的道理,各自有各自優(yōu)勢。
接下來我們一起來聽聽 express 和 koa 獨(dú)白:
express:hi,koa,我倆同處一體,我倆比比?
koa:???
express:我倆的發(fā)動(dòng)機(jī)都是中間件,我有三個(gè)兄弟:request,response,next,你有幾個(gè)兄弟呢?
koa:額,我有兩個(gè)兄弟:context,next。不過我這 context 兄弟很厲害,以一敵二(request,response)。
express:我自帶路由(Router),直接使用即可,你呢?
koa:安裝。(koa-router 或者 @dva/router)
express:我可以自己暴露靜態(tài)資源,你呢?
koa:安裝。(koa-static)
express:我只需要配置一下,就可以解析客戶端傳遞 application/json 的格式數(shù)據(jù),你呢?
koa:還是安裝。(koa-bodyparser)
express:你能不能不要安裝呀,你就沒有自帶的?我還是只需要配置一下,就可以解析客戶端傳遞 x-www-form-urlencoded 的格式數(shù)據(jù),你呢?
koa:哈哈哈,不好意思,我不用配置,我也能解析 x-www-form-urlencoded 的格式數(shù)據(jù)。
koa:我還能安裝 @koa/multer 來實(shí)現(xiàn)文件上傳,你自帶了嗎?
express:額。。。我這個(gè)還真沒自帶,我也需要安裝 multer 來實(shí)現(xiàn)。
koa:讓你裝 xxx。你我本是同根生,相煎何太急,和平相處不行嘛。
express:。。。
TJ:莫吵了,把你倆創(chuàng)建出來,設(shè)計(jì)理念本就是不相同的。express 你走的完整路線,koa 走的是靈活路線,所以不用相互較勁,和氣生財(cái)。
koa 和 express 的中間件對(duì)比
express 和 koa 的核心,都是中間件。
簡單的來說,理解了中間件,也就理解了express 和 koa。
何為中間件?
那么何為中間件呢?
express 和 koa 都能創(chuàng)建一個(gè)服務(wù)器實(shí)例 app,那么給 app 傳入一個(gè)回調(diào)函數(shù),那么該回調(diào)函數(shù)就被稱為中間件(middleware)。
express 創(chuàng)建中間件:
// 方式一:use(fn)
app.use((req, res, next) => {})
// 方式二:use(path, fn)
app.use('/', (req, res, next) => {})
// 方式三:method(path, fn)
app.get('/', (req, res, next) => {})
// 方式四:method(path, fn1, fn2, fn3)
app.get('/', (req, res, next) => {}, (req, res, next) => {})koa 創(chuàng)建中間件:
// 方式一:use(fn)
app.use((ctx, next) => {})
// 方式二:use(path, fn)
app.use('/', (ctx, next) => {})在這里就不展開怎么使用中間件了,我相信你會(huì)的,express 和 koa 的中間件道理是相同的。
中間件的執(zhí)行順序(同步,異步)
express 同步
app.use((req, res, next) => {
console.log("中間件01: next前");
next();
console.log("中間件01: next后");
});
app.use((req, res, next) => {
console.log("中間件02: next前");
next();
console.log("中間件02: next后");
});
app.use((req, res, next) => {
console.log("中間件03")
});
express 異步
function mockAsync () {
return new Promise((resolve) => {
setTimeout(() => {
resolve(321)
}, 1000)
})
}
app.use((req, res, next) => {
console.log("中間件01: next前");
next();
console.log("中間件01: next后");
});
app.use((req, res, next) => {
console.log("中間件02: next前");
next();
console.log("中間件02: next后");
});
app.use(async (req, res, next) => {
const data = await mockAsync()
console.log("中間件03: next前");
});
koa 同步
app.use((ctx, next) => {
console.log('中間件01: next前');
next()
console.log('中間件01: next后');
})
app.use((ctx, next) => {
console.log("中間件02: next前");
next();
console.log("中間件02: next后");
});
app.use((ctx, next) => {
console.log("中間件03");
});
koa 異步
function mockAsync() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(321);
}, 1000);
});
}
app.use((ctx, next) => {
console.log('中間件01: next前');
next()
console.log('中間件01: next后');
})
app.use((ctx, next) => {
console.log("中間件02: next前");
next();
console.log("中間件02: next后");
});
app.use(async (ctx, next) => {
const res = await mockAsync()
console.log("中間件03");
});
上面四個(gè)案例,分別從:
- express 同步
- express 異步
- koa 同步
- koa 異步
來分析了中間的執(zhí)行順序,可以得出兩點(diǎn)結(jié)論:
- 無論是 express 還是 koa,當(dāng)中間件是同步代碼并且調(diào)用了 next 函數(shù),那么程序運(yùn)行就會(huì)先執(zhí)行每個(gè)中間件next函數(shù)之前的代碼,當(dāng)執(zhí)行到最后一個(gè)中間件時(shí),又會(huì)回滾執(zhí)行每個(gè)中間件next 函數(shù)后的代碼(類似于 數(shù)據(jù)結(jié)構(gòu)中的 first in last out)。
- 無論是 express 還是 koa,當(dāng)遇到中間件中存在異步代碼,就會(huì)停止向下執(zhí)行,而是回到上一個(gè)中間件繼續(xù)執(zhí)行。
所以對(duì)于 express 和 koa 在中間件執(zhí)行上,**表現(xiàn)形式**上是相同的。
而不相同之處就是在于 express 和 koa 在針對(duì)中間件存在異步代碼時(shí),**處理方式**不同(簡單的來說也就是內(nèi)部的 next 函數(shù)實(shí)現(xiàn)不同)。
koa 和 express 不同的異步處理方式
假如存在這樣的一個(gè)案例:存在兩個(gè)中間件,一個(gè)是業(yè)務(wù)接口處理的中間件,一個(gè)是針對(duì)處理相同數(shù)據(jù)的中間件(比如:針對(duì)每個(gè)接口返回用戶信息),這里的獲取用戶信息,就是異步操作。
那么針對(duì) express 會(huì)寫出以下代碼:
function getUserInfo () {
return new Promise((resolve) => {
setTimeout(() => {
resolve({name: 'copyer', sex: 'man'})
}, 1000)
})
}
app.get('/list', (req, res, next) => {
const list = [
{ id: "1", content: "列表1" },
{ id: "2", content: "列表2" },
];
next();
res.json({
list,
userInfo: req?.userInfo // 返回用戶信息,需要通過下個(gè)中間件來獲取
})
});
app.use(async (req, res, next) => {
// mock 異步代碼,拿到用戶信息
const data = await getUserInfo();
console.log(data); // { name: 'copyer', sex: 'man' }
req.userInfo = data; // 設(shè)置用戶信息
});當(dāng)我們?cè)L問 list 接口時(shí),發(fā)現(xiàn)是拿不到用戶信息(userInfo),因?yàn)橛龅绞钱惒胶瘮?shù),就會(huì)停止繼續(xù)執(zhí)行下面的代碼,而是回到上一個(gè)中間件繼續(xù)執(zhí)行,所以沒有拿到用戶信息。
koa 也是同樣的道理,但是 koa 卻是可以解決這個(gè)問題。代碼如下:
function getUserInfo() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ name: "copyer", sex: "man" });
}, 1000);
});
}
router.get('/list', async (ctx, next) => {
const list = [
{ id: "1", content: "列表1" },
{ id: "2", content: "列表2" },
];
await next(); /*****************重點(diǎn)代碼*********************/
ctx.body = {
list,
ctx: ctx?.userInfo
}
});
router.use(async (ctx, next) => {
const res = await getUserInfo();
console.log(res); // { name: 'copyer', sex: 'man' }
ctx.userInfo = res; // 設(shè)置用戶信息
});
app.use(router.routes())
看上面標(biāo)記的重點(diǎn)代碼,那么就是處理異步的關(guān)鍵。在 next 執(zhí)行前面,加上一個(gè) await,這樣就能正常的拿到用戶信息。
await next() 的意思就是等待下個(gè)中間件執(zhí)行完成,那么這樣用戶信息也就設(shè)置成功了。
那么肯定有人這樣想,那么我在 express 調(diào)用 next 函數(shù)時(shí),也加上 await ,是不是也解決了?想法很美好,但是行不通的。為撒?
express 中的 next 函數(shù),返回值一個(gè)void,沒有返回值。
這里的 next 函數(shù)接受一個(gè)參數(shù) err,也就是錯(cuò)誤信息,針對(duì)全局異常處理的收集時(shí),就會(huì)使用。

koa 中的 next 函數(shù),返回值一個(gè)Promise。
這里的 next 函數(shù)不接受參數(shù),所以全局錯(cuò)誤的異常處理,需要另想它法。

koa 和 express 在異步處理函數(shù),最大的差別就是 next 函數(shù)實(shí)現(xiàn)不同,那么也就造成了中間件在異步上的使用不同。
koa 在上一個(gè)中間件拿取下一個(gè)異步中間件的數(shù)據(jù),然后返回。express 卻是不行,這是 express 設(shè)計(jì)上的缺陷。
洋蔥模型
想想平時(shí)生活中的洋蔥,是不是一層一層的。

中間件從上往下(從外往里),然后從下往上(從里往外)執(zhí)行,無論是同步還是異步都滿足,koa 是符合洋蔥模型的。
express 是在同步的時(shí)候是滿足洋蔥模型的,但是異步的時(shí)候卻是不能滿足洋蔥模型。
總結(jié)
這篇主要從設(shè)計(jì)架構(gòu)和中間件兩個(gè)方面來解釋說明 express 和 koa 之間的差異。
比較差異并不是為了證明誰好誰弱,而是為了另一方面來充分認(rèn)識(shí)框架隱藏的知識(shí)點(diǎn),加深自己理解。
到此這篇關(guān)于node.js中koa和express的差異對(duì)比的文章就介紹到這了,更多相關(guān)koa和express對(duì)比內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Node.js中Express框架使用axios同步請(qǐng)求(async+await)實(shí)現(xiàn)方法
- node.js使用express-jwt報(bào)錯(cuò):expressJWT?is?not?a?function解決
- Node.js使用express寫接口的具體代碼
- Node.js?express中的身份認(rèn)證的實(shí)現(xiàn)
- 使用Express+Node.js對(duì)mysql進(jìn)行增改查操作?
- node.js三個(gè)步驟實(shí)現(xiàn)一個(gè)服務(wù)器及Express包使用
- Node.js中Express框架的使用教程詳解
- node.js+express留言板功能實(shí)現(xiàn)示例
- node.js使用express-fileupload中間件實(shí)現(xiàn)文件上傳
- Node.js+express+socket實(shí)現(xiàn)在線實(shí)時(shí)多人聊天室
- Express框架實(shí)現(xiàn)簡單攔截器功能示例
相關(guān)文章
Node.js和MongoDB實(shí)現(xiàn)簡單日志分析系統(tǒng)
這篇文章主要介紹了Node.js和MongoDB實(shí)現(xiàn)簡單日志分析系統(tǒng),本文給出了服務(wù)器端、客戶端、圖表生成、Shell自動(dòng)執(zhí)行等功能的實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-04-04
nodejs 十六進(jìn)制字符串型數(shù)據(jù)與btye型數(shù)據(jù)相互轉(zhuǎn)換
這篇文章主要介紹了nodejs 十六進(jìn)制字符串型數(shù)據(jù)與btye型數(shù)據(jù)相互轉(zhuǎn)換,需要的朋友可以參考下2018-07-07
解決使用node命令提示:'node'不是內(nèi)部或外部命令,也不是可運(yùn)行的程序
最近在工作中遇到了個(gè)常見的問題,分享給大家,這篇文章主要給大家介紹了關(guān)于如何解決使用node命令提示:'node'不是內(nèi)部或外部命令,也不是可運(yùn)行的程序的相關(guān)資料,需要的朋友可以參考下2023-02-02
node.js中TCP Socket多進(jìn)程間的消息推送示例詳解
這篇文章主要給大家介紹了關(guān)于node.js中TCP Socket多進(jìn)程間的消息推送的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07
使用Node.js實(shí)現(xiàn)HTTP 206內(nèi)容分片的教程
這篇文章主要介紹了使用Node.js實(shí)現(xiàn)HTTP 206內(nèi)容分片的教程,Node.js是一款用于服務(wù)器端的JavaScript框架,需要的朋友可以參考下2015-06-06
Node.js+ELK日志規(guī)范的實(shí)現(xiàn)
這篇文章主要介紹了Node.js+ELK日志規(guī)范的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05

