内容简介:在Koa2中,中间件被存放在一个数组中。 使用koa中,最常见的就是app.use(fn),use函数部分源码如下所示。首先中间件必须是个函数。若是generator函数,则需要进行转化。最后把该中间件推入middelaware数组中。当调用app.listen函数时,实际上是创建了一个原生http服务器,并执行了Koa自身的callback方法。callback函数中compose是将中间件封装成一个迭代器,按照middleware数组中顺序执行下去,实现了面向切面编程。handleRequest函数则
在Koa2中,中间件被存放在一个数组中。 使用koa中,最常见的就是app.use(fn),use函数部分源码如下所示。首先中间件必须是个函数。若是generator函数,则需要进行转化。最后把该中间件推入middelaware数组中。
constructor() { this.middleware = []; } use(fn) { this.middleware.push(fn); } 复制代码
当调用app.listen函数时,实际上是创建了一个原生http服务器,并执行了Koa自身的callback方法。
listen(...args) { const server = http.createServer(this.callback()); return server.listen(...args); } 复制代码
callback函数中compose是将中间件封装成一个迭代器,按照middleware数组中顺序执行下去,实现了面向切面编程。handleRequest函数则是将req和res封装成ctx,并执行中间件。
callback() { const fn = compose(this.middleware); const handleRequest = (req, res) => { const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); }; return handleRequest; } handleRequest(ctx, fnMiddleware) { ... return fnMiddleware(ctx).then(handleResponse).catch(onerror); } 复制代码
compose用来返回一个迭代器函数fnMiddleware,该函数接受两个参数,context和next。因为调用时只向fnMiddleware传入了第一个参数context,所以next参数的值一直是undefined。
首先定义了index,dispatch传入的参数 i 作为每个中间件的调用标志,每次中间件调用next函数都会让index+1,若是中间件多次调用next,则会使index大于等于该标志,就会报错。
然后依次读取中间件数组中的中间件,并将context和封装了调用标志i的dispatch函数传给中间件,就实现了中间件的过程。
当读取完中间件数组后,即i === middleware.length时,将值为是undefined的next传给fn并结束迭代。dispatch函数所有的返回值都是Promise函数,这样就可以实现异步编程。
function compose (middleware) { 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 if (!fn) return Promise.resolve() try { return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { return Promise.reject(err) } } } } 复制代码
二、Redux中间件源码分析
在Redux中需要引入中间件的话,需引入applyMiddleware函数并将其作为参数传给createStore。下方enhancer就是就是applyMiddleware的返回值。
export default function createStore(reducer, preloadedState, enhancer) { ... return enhancer(createStore)(reducer, preloadedState) } 复制代码
applyMiddleware接受一个中间件数组为参数。
export default function applyMiddleware(...middlewares) { return createStore => (...args) => { const store = createStore(...args) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } const chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } } 复制代码
其中compose用Array.prototype.reduce将中间件数组封装成一个层层包裹的函数。
function compose(...funcs) { return funcs.reduce((a, b) => (...args) => a(b(...args))) } 复制代码
若本来的中间件数组是
[a, b, c]
经过compose函数封装后,就成了
(...arg) => a(b(c(...arg)))
dispatch = compose(...chain)(store.dispatch) 复制代码
经过这个过程后,每次调用dispatch就会在中间件中进行遍历。每个中间件都需要调用next(action)来保证中间件链都能被执行。
Redux中中间件的写法一般为
const middleware = store => next => action => {...} 复制代码
根据上面的分析,store参数就是middlewareAPI,但是其中的dispatch并不是真正的dispatch,这是为了防止在中间件中调用store.dispatch而导致重新遍历整个中间件链。next是下一个中间件,需要传递action,直到最后一个中间件时,next即是原始的store.dispatch。
三、Koa2和Redux中间件比较
在两者中都出现了compose函数。
Koa2中的compose函数实现原理是用dispatch函数自身迭代,Redux中的compose实现原理是用了数组的reduce方法。两者都按照数组顺序执行中间件,并且先执行的中间件可以获取后执行的中间件的状态(store的状态或者context的状态),实现了面向切面编程。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 消息中间件 RocketMQ 源码解析 —— 调试环境搭建
- koa2第二篇: 图解中间件源码执行过程
- Laravel HTTP——SubstituteBindings 中间件的使用与源码解析
- Laravel HTTP——Pipeline 中间件装饰者模式源码分析
- 捕获异常URL--scrapy 源码分析之retry中间件
- Laravel HTTP——路由中间件的别名解析与排序源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
用户力:需求驱动的产品、运营和商业模式
郝志中 / 机械工业出版社 / 2015-11-1 / 59.00
《用户力:需求驱动的产品、运营和商业模式》从用户需求角度深刻阐释了互联网产品设计、网络运营、商业模式构建的本质与方法论! 本书以“用户需求”为主线,先用逆向思维进行倒推,从本质的角度分析了用户的需求是如何驱动企业的产品设计、网络运营和商业模式构建的,将这三个重要部分进行了系统性和结构化的串联,然后用顺向思维进行铺陈,从实践和方法论的角度总结了企业究竟应该如围绕用户的真实需求来进行产品设计、网......一起来看看 《用户力:需求驱动的产品、运营和商业模式》 这本书的介绍吧!