概述
作为一枚前端开发,经常会接触到中间件,今天咱们就来分析分析,中间件是怎么转起来的。
我们经常接触的中间件其实主要就是express、koa和redux,前面两个是服务端的中间件。
要实现一套middleware,分为三步:
- 搜集middleware
- 组合middleware
- 调用执行
中间件使用对比
直接上代码:
express:
1 | var express = require('express'); |
koa:
1 | var Koa = require('koa'); |
redux:
1 | var redux = require('redux') |
中间件源码实现对比
express
1、收集middleware
express全局维护一个stack数组用来存储所有中间件,app.use进行收集。
1 | // application.js |
2、组合middleware
express通过app.handle 即对 stack 数组进行处理,next 方法不断的取出stack中的“中间件”函数进行调用,同时把next 本身传递给“中间件”作为第三个参数,形成尾递归调用
1 | // application.js |
3、调用执行
整个 web server的入口处理程序是 express.js 文件中的 app,引入的express方法就是createApplication,用于创建 express 实例。
1 | // express.js |
返回值 app 既是实例对象,上面挂载了很多方法,同时也是一个方法,作为 http.createServer的回调处理函数
1 | // application.js |
这里 app.listen 调用 nodejs 的http.createServer 创建web服务,可以看到这里 var server = http.createServer(this) 其中 this 即 app 本身, 然后真正的处理程序即 app.handle;
整个express实现就到此揭开面纱了。
koa
1、收集middleware
koa也是通过use进行收集。
1 | // application.js |
不难看出,构造函数 constructor 中维护全局中间件数组 this.middleware和全局的this.context 实例; use就做了一件事:把fn push到Koa的middleware数组中。
2、组合middleware
中间件如何建立起了洋葱模型,其实就是依靠compose,Koa的compose来自于koa-compose项目,源码也比较简单:
1 | function compose (middleware) { |
3、调用执行
首先我们只要知道下面这段callback函数就是请求到来的时候执行的回调
1 | // application.js |
请求前的中间件初始化处理部分(本质就是对中间件集合建立起一个洋葱模型) + 请求到来时的中间件运行部分(fn(ctx).then(handleResponse).catch(onerror);)
到此呢,Koa中间件的面纱就完全揭开了。
redux
1、收集middleware
看完redux的使用方法,很明显可以看出redux收集middleware是通过applyMiddleware实现,我们看看他的源码:
1 | // applyMiddleware.js |
可以看出它接受一系列middleware作为参数,返回一个接受createStore作为参数的高阶方法。(createStore的 enhance 方法);内部即增加了对中间件的应用
createStore 接收一个 enhancer 参数用于自增强
1 | store = createStore(reducer, initState, applyMiddleware(middleware1, middleware2)) |
再来看看createStore源码
1 | function createStore(reducer, preloadedState, enhancer) { |
中间件形式 store => next => action => { xx }是函数柯里化作用的灵活体现,将多参数化为单参数,可以用于提前固定 store 参数,得到形式更加明确的 dispatch => dispatch
当调用dispatch的时候, 上面的中间件将会自动触发
现在在来理解 dispatch = compose(...chain)(store.dispatch) 就相对容易了,原生的 store.dispatch 传入最后一个“中间件”,返回一个新的 dispatch , 再向外传递到前一个中间件,直至返回最终的 dispatch, 当覆写后的dispatch 调用时,每个“中间件“的执行又是从外向内的”洋葱圈“模型。
总结
总体而言,express 和 koa 的实现很类似,都是next 方法传递进行递归调用,只不过 koa 是promise 形式。redux 相较前两者有些许不同,先通过递归向外覆写,形成执行时递归向里调用。
总结一下三者关键异同点(不仅限于中间件):
1、实例创建: express 使用工厂方法, koa是类
2、koa 实现的语法更高级,使用ES6,支持async await(generator)
3、koa 没有内置router, 增加了 ctx 全局对象,整体代码更简洁,使用更方便。
4、koa 中间件的递归为 promise形式,express 使用while 循环加 next 尾递归
5、redux是柯里化中间件形式,更简洁灵活,函数式编程体现的更明显
redux 以 dispatch 覆写的方式进行中间件增强
参考: https://juejin.im/post/5b9a23a45188255c9c751b07#heading-6