Home >Web Front-end >JS Tutorial >Compare nodejs middleware Koa and Express

Compare nodejs middleware Koa and Express

青灯夜游
青灯夜游forward
2021-02-24 09:52:142358browse

Compare nodejs middleware Koa and Express

Related recommendations: "nodejs Tutorial"

Speaking of middleware, many developers will think of Koa.js, and its middleware design is undoubtedly It is one of the typical representatives of front-end middleware thinking.

Recently reviewing this part of the content, I couldn’t help but want to chat with you readers about its wonderfulness!


Koa is very convenient to use - compared to express, its "perfect middleware" design makes the functions look very simple! The author has used it like this in the project:

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('服务已开启')
})

It extracts the routing router and uses it as a separate middleware, and the app is only responsible for global processing. Another example:

// 最外层中间件,可以用于兜底 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)}`);
});

Simply implement a Koa!

As shown in the above code, we look at the Koa instance and register and concatenate the middleware through the use method. The simple implementation of the source code can be expressed as:

use(fn) {
    this.middleware.push(fn);
    return this;
}

We store the middleware in this.middlewareIn the array, how is the middleware executed? Refer to the source code below:

// 通过 createServer 方法启动一个 Node.js 服务
listen(...args) {
    const server = http.createServer(this.callback());
    server.listen(...args);
}

Koa framework creates a Node.js service through the createServer method of the http module, and passes in the this.callback() method, callback source code The simple implementation is as follows:

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);
}

As shown in the above code, we organize the Koa middleware combination and execution process into the following steps:

  • Through a method (we call it compose) Combine various middleware and return a middleware combination function fn

  • When a request comes, the handleRequest method will be called first, and the method is completed :

    • Call the createContext method to encapsulate a ctx object for this request;
    • then call this.handleRequest(ctx, fn)Process this request.

The core process is to use the compose method to combine various middlewares - this is a separate method, and it should not be constrained by the rest of Koa's methods. Its source code is simply implemented as:

// 组合中间件
// 和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)
	}
}

Its function can be expressed as follows (non-source code):

async function middleware1() {
  //...
  await (async function middleware2() {
    //...
    await (async function middleware3() {
      //...
    });
    //...
  });
  //...
}

At this point we can actually get a "first glimpse" of its principle, there are two points:

  • Koa’s middleware mechanism is vividly summarized by the community as an onion model;

The so-called onion model means that each Koa middleware is a layer of onion rings. It can handle both request entry and response return. In other words: the outer middleware can affect the request and response phases of the inner layer, and the inner middleware can only affect the response phase of the outer layer.

  • dispatch(n) corresponds to the execution of the nth middleware. When in use, the nth middleware can be "inserted" to execute the next middleware through await next(). At the same time, after the execution of the last middleware is completed, there is still the ability to resume execution. That is: through the onion model, await next() controls the calling of subsequent middleware until there is no executable middleware in the world and the stack execution is completed, and finally "returns to the original path" to the first middleware that executes next. This method has advantages, especially for global functions such as logging and error handling that need to be very friendly.

Koa1’s middleware implementation uses the Generator function co library (a Generator function process management tool based on Promise) to implement coroutine running. In essence, the ideas of Koa v1 middleware and Koa v2 middleware are similar, except that Koa v2 uses Async/Await to replace the Generator function co library. The overall implementation is more clever and the code is more elegant. —— from "Wolf Book"

After describing the above part of the source code, we can combine it using 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

The use method is not the same as other methods. Interlinked, how is it executed? After executing createServer, is it equivalent to establishing a channel and mounting a listening function?
I’m afraid we need to find out more about this in the source code of Node...


Compare Koa and talk about the principles of Express

Speaking of Node. js framework, we must not forget Express - unlike Koa, it inherits functions such as routing, static server and template engine. Although it is much "bloated" compared to Koa, it looks more like a framework than Koa. By studying the Express source code, the author simply summarized its working mechanism:

  • Register middleware through the app.use method.

  • A middleware can be understood as a Layer object, which contains the regular information matched by the current route and the handle method.

  • All middleware (Layer objects) are stored using the stack array.

  • When a request comes, the request path will be obtained from req, and the matching Layer will be found from the stack according to the path. The specific matching process is performed by the router.handle function accomplish.

  • router.handleThe function traverses each layer for comparison through the next() method:

    • 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的简单实现

更多编程相关知识,请访问:编程视频!!

The above is the detailed content of Compare nodejs middleware Koa and Express. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:csdn.net. If there is any infringement, please contact admin@php.cn delete