搜尋
首頁web前端js教程比較一下nodejs中間件Koa和Express
比較一下nodejs中間件Koa和ExpressFeb 24, 2021 am 09:52 AM
expressnode

比較一下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中文網其他相關文章!

陳述
本文轉載於:csdn。如有侵權,請聯絡admin@php.cn刪除
node、nvm与npm有什么区别node、nvm与npm有什么区别Jul 04, 2022 pm 04:24 PM

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

Vercel是什么?怎么部署Node服务?Vercel是什么?怎么部署Node服务?May 07, 2022 pm 09:34 PM

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

node爬取数据实例:聊聊怎么抓取小说章节node爬取数据实例:聊聊怎么抓取小说章节May 02, 2022 am 10:00 AM

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

node导出模块有哪两种方式node导出模块有哪两种方式Apr 22, 2022 pm 02:57 PM

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

安装node时会自动安装npm吗安装node时会自动安装npm吗Apr 27, 2022 pm 03:51 PM

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

node中是否包含dom和bomnode中是否包含dom和bomJul 06, 2022 am 10:19 AM

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

聊聊V8的内存管理与垃圾回收算法聊聊V8的内存管理与垃圾回收算法Apr 27, 2022 pm 08:44 PM

本篇文章带大家了解一下V8引擎的内存管理与垃圾回收算法,希望对大家有所帮助!

聊聊Node.js path模块中的常用工具函数聊聊Node.js path模块中的常用工具函数Jun 08, 2022 pm 05:37 PM

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

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
2 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
2 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
2 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3 英文版

SublimeText3 英文版

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

mPDF

mPDF

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

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

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