相關推薦:《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中文網其他相關文章!

node、nvm与npm的区别:1、nodejs是项目开发时所需要的代码库,nvm是nodejs版本管理工具,npm是nodejs包管理工具;2、nodejs能够使得javascript能够脱离浏览器运行,nvm能够管理nodejs和npm的版本,npm能够管理nodejs的第三方插件。

Vercel是什么?本篇文章带大家了解一下Vercel,并介绍一下在Vercel中部署 Node 服务的方法,希望对大家有所帮助!

node怎么爬取数据?下面本篇文章给大家分享一个node爬虫实例,聊聊利用node抓取小说章节的方法,希望对大家有所帮助!

node导出模块的两种方式:1、利用exports,该方法可以通过添加属性的方式导出,并且可以导出多个成员;2、利用“module.exports”,该方法可以直接通过为“module.exports”赋值的方式导出模块,只能导出单个成员。

安装node时会自动安装npm;npm是nodejs平台默认的包管理工具,新版本的nodejs已经集成了npm,所以npm会随同nodejs一起安装,安装完成后可以利用“npm -v”命令查看是否安装成功。

node中没有包含dom和bom;bom是指浏览器对象模型,bom是指文档对象模型,而node中采用ecmascript进行编码,并且没有浏览器也没有文档,是JavaScript运行在后端的环境平台,因此node中没有包含dom和bom。

本篇文章带大家聊聊Node.js中的path模块,介绍一下path的常见使用场景、执行机制,以及常用工具函数,希望对大家有所帮助!


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

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

Atom編輯器mac版下載
最受歡迎的的開源編輯器

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。