koa/redux middleware系统解析
来源:互联网 发布:华军软件下载 编辑:程序博客网 时间:2024/06/11 01:20
middleware
对于现有的一些框架比如koa,express,redux,都需要对数据流进行一些处理,比如koa,express的请求数据处理,包括json.stringify,logger,或者一些安全相关的处理都需要在数据流中进行,还比如redux的整个数据的修改,支持中间件来扩展用户对于数据修改的支持。
middleware系统是处理流式数据的利器,实现方便,功能强大。
本文就分别研究一下redux的koa的middleware系统~
redux
对于redux,一个数据处理中心,它的用法想必大家已经很熟了。
import thunk from "redux-thunk";import {applyMiddleware,createStore} from "redux";const createStoreWithMiddleware = applyMiddleware(thunk)(createStore)
我们直接上redux源码:
function applyMiddleware() { for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) { //跟compose一样处理参数 middlewares[_key] = arguments[_key]; } return function (createStore) { return function (reducer, initialState, enhancer) { var store = createStore(reducer, initialState, enhancer);//传入createStore方法 var _dispatch = store.dispatch; //重要的dispatch方法 var chain = []; var middlewareAPI = { //需要传入middleware的reduxAPI getState: store.getState, dispatch: function dispatch(action) { return _dispatch(action); } }; chain = middlewares.map(function (middleware) { //遍历每个中间件把reduxAPI传进去。返回一个封装好的中间件 return middleware(middlewareAPI); }); _dispatch = _compose2["default"].apply(undefined, chain)(store.dispatch); //相当于compose(...chain)(store.dispatch) return _extends({}, store, { //然后给store重写的这个执行完中间件的dispatch方法。 dispatch: _dispatch }); }; };}
我们这里只传了一个thunk中间件,当然也可以传多个middleware,可以看到源码里把applyMiddleware所有参数保存为中间件。
我们只讲重要的middleware应用部分。对于中间件的处理,redux重写了dispatch方法,在dispatch action的时候先经过一遍中间件,流式的通过中间件处理,再进行dispatch。核心代码就是_dispatch = _compose2["default"].apply(undefined, chain)(store.dispatch);
,其实就是compose(...chain)(store.dispatch)
。
我们用thunk来具体讲一下流程。
thunk的源码是:
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); };}const thunk = createThunkMiddleware();thunk.withExtraArgument = createThunkMiddleware;export default thunk;
实际我们引入过来在redux源码里的就是
({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); };
之间通过chain处理过middlmiddleware,所以chain里的各个middleware其实是这样的:
next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); };
根据compose的作用,我们实际获得的_dispatch是这样的:
如果只有一个middleware,我们传进来store.dispatch,那么_dispatch为:
action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return dispatch(action); };
如果有两个middleware(我们实际在演示compose的处理),我们得到的其实是。。。之前我们先简化一下middleware:
function middleware(next){ return function(action){ return { // before next(action); // xxx; } }}
第二个middleware传进来我们得到的是:
function (){ return function(action){ return { (function(action){ return { // before dispatch(action); // xxx } })(action) } }}
第三个middleware:
function (){ return function(action){ return { (function(action){ //before 1 return (function(action){ return { // before 2 dispatch(action); // xxx 2 } })(action) // xxx 1 })(action) } }}
我们会发现,_dispatch触发之后,需要经过各个自执行的中间件。
我们还会发现,我们的执行顺序是根据compose的执行顺序来的,但是在中间件调用之后并不会返回,我们还会执行next之后的‘xxx’代码。而它的代码顺序是相反的。
但是我们并不会在next之后执行命令阿?我们的规范也都是以next结尾。
原因?
next的后的代码其实是可以用的,但是有一点问题就是还是执行顺序的问题,如果某个中间件是异步执行,执行顺序就无法保证了。
这个情况一直持续到es6 generator函数的出现。。。
koa
对于koa,1.x的时候支持了generator,现在支持了async函数,所以现在的koa的中间件系统是“洋葱圈”式的处理方式,也就是上面的先执行每个中间件的before,再倒叙执行xxx函数。
有个图直观的感受一下:
所有的请求经过一个中间件的时候都会执行两次。
因为koa的每个middleware是无关的,所以我们并不需要像redux的compose一样用reduceRight实现,它的compose实现一会再谈,先谈一下middleware的工作流程吧。(redux的compose实现之前博文有讲)
const server = http.createServer(this.callback()); server.listen(...args);const fn = compose(this.middleware);const handleRequest = (req, res) => { res.statusCode = 404; const ctx = this.createContext(req, res); const onerror = err => ctx.onerror(err); const handleResponse = () => respond(ctx); onFinished(res, onerror); return fn(ctx).then(handleResponse).catch(onerror); };
我们构建一个服务,请求来的时候传入一个上下文环境给中间件,然后请求通过中间件处理。
koa的中间件形式就是图上那种的格式,我这里只讲一下最新的async的处理模式吧,因为此源码是基于aysnc的处理。
举个middleware的例子:
async function middleware(ctx, next){ // before await next(); // xxx;}
app.use其实判断一下middleware然后push进this.middleware,没有什么好说的。
对于这种“洋葱圈”式的处理方式,主要就是koajs/compose处理的。
它是怎么做的呢? koajs/koa
function compose (middleware) { //首先保证middleware是数组,它的每一项都是函数 if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!') for (const fn of middleware) { if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!') } /** * @param {Object} context * @return {Promise} * @api public */ return function (context, next) { // last called middleware # let index = -1 return dispatch(0) function dispatch (i) { if (i <= index) return Promise.reject(new Error('next() called multiple times')) index = i let fn = middleware[i] if (i === middleware.length) fn = next // 如果传了next,处理完所有middleware之后调用。 if (!fn) return Promise.resolve() // 如果为空或者调用完返回空的promise函数。 try { return Promise.resolve(fn(context, function next () { return dispatch(i + 1) // 尾递归调用下个middleware })) } catch (err) { return Promise.reject(err) } } }}
这个compose比较简单,因为组件间的关联从返回值变成了context。koa之间中间件的联系应该就是一个全局通用的context参数了。也是koa推荐写法。
这个compose就是递归调用所有的middleware。
值得一提的点就是koa为async函数特制的compose函数,async函数的awiat需要每次异步都是一个promise,如果为值,那就是同步处理。所以返回的middleware都被包了一层Promise.resolve。
它的处理过程就是:
一个middleware:
async function middleware(ctx, next){ // before await next(); // xxx;}
两个middleware:
async function middleware(ctx, next){ // before // before2 await next2(); // xxx2; // xxx;}
next之后的xxx函数的调用顺序保证得益于async的函数执行顺序。且把await看做then的语法糖。
优势
middleware的好处就不提了。
只说一下koa的这种实现的两个好处:
- 并行优化
- 错误捕获机制
- 写起来很漂亮
错误捕获机制也是利用promise的优势:
如果resolve的参数是Promise对象,则该对象最终的[[PromiseValue]]会传递给外层Promise对象后续的then的onFulfilled/onRejected
- koa/redux middleware系统解析
- koa/redux middleware 深入解析
- Redux Middleware
- redux middleware 详解
- redux middleware 的理解
- redux middleware 详解
- redux的中间件(middleware)
- Koa中间件(middleware)实现探索
- redux深入理解之中间件(middleware)
- redux深入理解之中间件(middleware)
- redux深入理解之中间件(middleware)
- 为 Koa 框架封装 webpack-dev-middleware 中间件
- Redux解析
- Django源码解析:middleware
- React实战-深入源码了解Redux用法之Middleware
- PHP中间件(middleware)解析
- PHP中间件(middleware)解析
- Koa
- matlab2c使用c++实现matlab函数系列教程-exp函数
- 邝斌的ACM模板(整数拆分)
- 网络编程-TCP粘包
- 欢迎使用CSDN-markdown编辑器
- a服务调b服务的接口,postForEntity传递参数,在服务端显示乱码
- koa/redux middleware系统解析
- JVM菜鸟进阶高手之路十(基础知识开场白)
- hibernate实体类的三种状态
- Hibernate-2017-09-04
- mySQL之库、表、记录增删改查
- [NOIP2017模拟]树
- poj 1777 梅森素数
- idea创建springboot项目图文教程(配置文件)(五)
- matlab2c使用c++实现matlab函数系列教程-fix函数