比較一下nodejs中間件Koa和Express
相關推薦:《nodejs 教學》
說到中間件,許多開發者都會想到Koa.js,其中間件設計無疑是前端中間件思想的典型代表之一。
最近重新溫習這部分內容,按奈不住想要和各位看官聊聊其中絕妙!
Koa用起來非常方便-比之express,它「完美中間件」的設計讓功能之間看起來非常簡潔!筆者在專案中就曾這樣使用過:
const Koa=require('koa') const app=new Koa() const Router=require('koa-router') const router=new Router() const cors=require('koa2-cors') const koaBody=require('koa-body') const ENV='test-mpin2' app.use(cors({ origin:['http://localhost:9528'], // 也可以写为:['*'] credentials:true })) app.use(koaBody({ multipart:true })) app.use(async(ctx,next)=>{ console.log('访问全局中间件') ctx.state.env=ENV // 全局缓存 await next() }) const playlist=require('./controller/playlist.js') router.use('/playlist',playlist.routes()) const blog=require('./controller/blog.js') router.use('/blog',blog.routes()) app.use(router.routes()).use(router.allowedMethods()) app.listen(3000,()=>{ console.log('服务已开启') })
它將路由router抽離出去作為單獨的中間件使用,則app只負責全局處理。還例如:
// 最外层中间件,可以用于兜底 Koa 全局错误 app.use(async (ctx, next) => { try { // 执行下一个中间件 await next(); } catch (error) { console.log(`[koa error]: ${error.message}`) } }); // 第二层中间件,可以用于日志记录 app.use(async (ctx, next) => { const { req } = ctx; console.log(`req is ${JSON.stringify(req)}`); await next(); console.log(`res is ${JSON.stringify(ctx.res)}`); });
簡單實作一個Koa吧!
如上程式碼,我們看Koa 實例,透過use方法註冊和串聯中間件,其原始碼的簡單實作可以表述為:
use(fn) { this.middleware.push(fn); return this; }
我們將中間件儲存到this.middleware
數組中,那麼中間件是如何被執行的呢?參考下面原始碼:
// 通过 createServer 方法启动一个 Node.js 服务 listen(...args) { const server = http.createServer(this.callback()); server.listen(...args); }
Koa 框架透過http 模組的createServer
方法建立一個Node.js 服務,並傳入this.callback()
方法, callback原始碼簡單實作如下:
callback(){ const fn=compose(this.middlewareList) return (req,res)=>{ const ctx=createContext(req,res) return this.handleRequest(ctx,fn) } } handleRequest(ctx, fn) { const onerror = err => ctx.onerror(err); // 将 ctx 对象传递给中间件函数 fn return fn(ctx).catch(onerror); }
如上程式碼,我們將Koa 一個中間件組合和執行流程梳理為以下步驟:
透過一個方法(我們稱為compose)組合各種中間件,回傳一個中間件組合函數
fn
請求過來時,會先呼叫
handleRequest
方法,該方法完成:- 呼叫
createContext
方法,對該次請求封裝出一個ctx物件; - 接著呼叫
this.handleRequest(ctx, fn)
處理該次請求。
- 呼叫
#其中,核心過程就是使用compose方法組合各種中間件 —— 這是一個單獨的方法,它應該不受Koa其餘方法的約束。其原始碼簡單實作為:
// 组合中间件 // 和express中的next函数意义一样 function compose(middlewareList){ // return function意思是返回一个函数 return function(ctx,next){ // 各种中间件调用的逻辑 function dispatch(i){ const fn=middlewareList[i] || next if(fn){ try{ // koa中都是async,其返回的是一个promise(对象) return Promise.resolve(fn(ctx,function next(){ return dispatch(i+1) })) }catch(err){ return Promise.reject(err) } }else{ return Promise.resolve() } } return dispatch(0) } }
其功能可以表示為這樣(非原始碼):
async function middleware1() { //... await (async function middleware2() { //... await (async function middleware3() { //... }); //... }); //... }
到這裡我們其實可以「初窺」其原理,有兩點:
- Koa 的中間件機制被社群形像地總結為洋蔥模型;
#所謂洋蔥模型,就是指每一個Koa 中間件都是一層洋蔥圈,它即可以掌管請求進入,也可以掌管回應返回。換句話說:外層的中介軟體可以影響內層的請求與回應階段,內層的中介軟體只能影響外層的回應階段。
- dispatch(n)對應第n 個中間件的執行,在使用中即第n 個中間件可以透過await next()來「插入」執行下一個中間件,同時在最後一個中間件執行完成後,仍有恢復執行的能力。即:透過洋蔥模型,await next()控制呼叫後面的中間件,直到全域沒有可執行的中間件且堆疊執行完畢,最終「原路返回」至第一個執行next的中間件。 這種方式有個優點,特別是對於日誌記錄以及錯誤處理等全域功能需要非常友善。
Koa1 的中間件實作利用了 Generator 函數 co 函式庫(一個基於 Promise 的 Generator 函數流程管理工具),來實現協程運行。本質上,Koa v1 中間件和 Koa v2 中間件思想是類似的,只不過 Koa v2 改用了 Async/Await 來替換 Generator 函數 co 庫,整體實現更加巧妙,代碼更加優雅。 —— from《狼書》
經過上述部分原始碼的描述,我們就可以採用es6的方式將其組合起來:
// myKoa.js文件 const http=require('http') function compose(){} //见上 class LikeKoa2{ constructor() { this.middlewareList=[] } use(){} //见上 // 把所有的req,res属性、事件都交给ctx(这里只是简写) createContext(req,res){ const ctx={ req, res } // 比如 ctx.query=req,query return ctx } handleRequest(){} //见上 callback(){} //见上 listen(){} //见上 } // koa和express的不同之一: // express在调用时直接调用函数:const app=express();所以暴露出去new过的对象——具体见下面链接中代码 // 但是koa调用时以类的方式:const app=new Koa();所以直接暴露出去 module.exports=LikeKoa2
那use方法和其餘方法並不相通,它是如何被執行的呢?執行了createServer後是不是相當於建立了一個通道、掛載了一個監聽函數呢?
這一點恐怕就要到Node的源碼中一探究竟了…
比較Koa,聊聊Express 原理
說起Node. js 框架,我們一定忘不了Express —— 不同於Koa,它繼承了路由、靜態伺服器和模板引擎等功能,雖然比之Koa顯得「臃腫」了許多,但看上去比Koa 更像是一個框架。透過學習 Express 原始碼,筆者簡單的總結了它的工作機制:
透過app.use方法註冊中間件。
一個中間件可以理解為一個 Layer 對象,其中包含了目前路由匹配的正規資訊以及 handle 方法。
所有中間件(Layer 物件)使用stack陣列儲存起來。
當一個請求過來時,會從req 取得請求path,根據path 從stack中找到匹配的Layer,具體匹配過程由
router.handle
函數實現。router.handle
函數透過next()
方法遍歷每一個 layer 進行比對:next()
方法通过闭包维持了对于 Stack Index 游标的引用,当调用next()
方法时,就会从下一个中间件开始查找;- 如果比对结果为 true,则调用
layer.handle_request
方法,layer.handle_request
方法中会调用next()方法 ,实现中间件的执行。
通过上述内容,我们可以看到,Express 其实是通过 next()
方法维护了遍历中间件列表的 Index 游标,中间件每次调用next()
方法时,会通过增加 Index 游标的方式找到下一个中间件并执行。它的功能就像这样:
((req, res) => { console.log('第一个中间件'); ((req, res) => { console.log('第二个中间件'); (async(req, res) => { console.log('第三个中间件'); await sleep(2000) res.status(200).send('hello') })(req, res) console.log('第二个中间件调用结束'); })(req, res) console.log('第一个中间件调用结束') })(req, res)
如上代码,Express 中间件设计并不是一个洋葱模型,它是基于回调实现的线形模型,不利于组合,不利于互操,在设计上并不像 Koa 一样简单。而且业务代码有一定程度的侵扰,甚至会造成不同中间件间的耦合。
express的简单实现笔者已上传至腾讯微云,需要者可自行查看&下载:express的简单实现
更多编程相关知识,请访问:编程视频!!
以上是比較一下nodejs中間件Koa和Express的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

nvm刪除node的方法:1、下載「nvm-setup.zip」並將其安裝在C碟;2、設定環境變量,並透過「nvm -v」指令查看版本號;3、使用「nvm install」指令安裝node;4、透過「nvm uninstall」指令刪除已安裝的node即可。

怎麼處理文件上傳?以下這篇文章為大家介紹一下node專案中如何使用express來處理文件的上傳,希望對大家有幫助!

這篇文章跟大家分享Node的進程管理工具“pm2”,聊聊為什麼需要pm2、安裝和使用pm2的方法,希望對大家有幫助!

PiNetwork節點詳解及安裝指南本文將詳細介紹PiNetwork生態系統中的關鍵角色——Pi節點,並提供安裝和配置的完整步驟。 Pi節點在PiNetwork區塊鏈測試網推出後,成為眾多先鋒積極參與測試的重要環節,為即將到來的主網發布做準備。如果您還不了解PiNetwork,請參考Pi幣是什麼?上市價格多少? Pi用途、挖礦及安全性分析。什麼是PiNetwork? PiNetwork項目始於2019年,擁有其專屬加密貨幣Pi幣。該項目旨在創建一個人人可參與

npm node gyp失敗是因為“node-gyp.js”跟“Node.js”版本不匹配,其解決辦法:1、透過“npm cache clean -f”清除node快取;2、透過“npm install -g n”安裝n模組;3、透過「n v12.21.0」指令安裝「node v12.21.0」版本即可。

身份驗證是任何網路應用程式中最重要的部分之一。本教程討論基於令牌的身份驗證系統以及它們與傳統登入系統的差異。在本教程結束時,您將看到一個用Angular和Node.js編寫的完整工作演示。傳統身份驗證系統在繼續基於令牌的身份驗證系統之前,讓我們先來看看傳統的身份驗證系統。使用者在登入表單中提供使用者名稱和密碼,然後點擊登入。發出請求後,透過查詢資料庫在後端驗證使用者。如果請求有效,則使用從資料庫中獲取的使用者資訊建立會話,然後在回應頭中傳回會話訊息,以便將會話ID儲存在瀏覽器中。提供用於存取應用程式中受

如何用pkg打包nodejs可執行檔?以下這篇文章跟大家介紹一下使用pkg將Node專案打包為執行檔的方法,希望對大家有幫助!

什麼是單一登入系統?用nodejs怎麼實作?以下這篇文章為大家介紹一下使用node實作單一登入系統的方法,希望對大家有幫助!
