深入理解redux中间件
<p>redux middleware 是 redux 的一个 advanced feature. 这个概念并不是很新奇,以为在 Koa 里面早已经实现过了. 对比与原生的redux middleware , koa 的 middleware 差不多相当于是爸爸级的 level 了. 这么说,是有依据的. 我们这里,来深入一下源码,具体看一下redux middleware 到底做了些啥.</p> <p>我们首先来探讨一下基本的源码吧.</p> <p>redux 的源码可以说是比较简单的。 首先,入口文件是 index.js 。我们来看一下,里面具体的内容:</p> <pre> <code class="language-javascript">// 从不同的文件里面导出相关方法 export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose }</code></pre> <p>这里,其实是 redux 的提供的所有方法了. createStore,combineReducers,bindActionCreators 这三个方法,与 middleware 关系不大,我这里就不赘述了. 这里,主要讲解一下 applyMiddleware 方法和 compose 方法.</p> <p>in fact, compose 是一个非常基础的方法, 用来以函数式的编程来组合中间件, 在 koa 中我们也同样遇见过这样的写法. applyMiddleware 也是用到这样的方法的. so, 我们来具体看看.</p> <h2><strong>compose 方法</strong></h2> <p>compose 的源码就是一个函数 compose :</p> <pre> <code class="language-javascript">export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } // 获取最后一个函数 const last = funcs[funcs.length - 1]; // 获取除最后一个以外的函数[0,length-1) const rest = funcs.slice(0, -1) // 通过函数 curry 化 return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) }</code></pre> <p>第一眼看的时候, 肯定超级S B。。。 md... 这写个啥... 看了一下官方给的注释就是:</p> <pre> <code class="language-javascript">// 简单 demo 就是 compose(f, g, h) is identical to doing (...args) => f(g(h(...args))).</code></pre> <p>合着就是个函数嵌套的写法。。。</p> <p>关键坑爹的在于他的reduceRight方法, 写的真心6. 由于,return 两个函数时,只会返回第二个执行的结果:</p> <pre> <code class="language-javascript">function test(a,b){ return a(),b(); } console.log(test(a=>1,b=>2)); // 开玩笑的. 上面那种只是科普一下. 他真正的机制实际上是利用 reduceRight 的第二个参数来执行的 Array.reduceRight(fn,start); // 主要还是这里的start, 相当于就是 last(...args) // 将上面代码翻译一下就是 rest.reduceRight(function(composed, f){return f(composed)}, last(...args)); //... 慢慢去体会吧...</code></pre> <p>所以, 一开始看的时候,在纠结 最后一个 composed 都没执行... 后来发现, 原来还有一层 last(...args).</p> <p>不过实话说, 真心没有 koa 里面的 compose 函数写得好, 你直接先写一个 noop 函数不行吗!!!</p> <pre> <code class="language-javascript">// 俺 实际写了一个替换的compose. 感觉这个看的清楚一点 function compose(...funcs) { return function(...args) { var len = funcs.length, middle = funcs[--len](...args); while (len--) { middle = funcs[len].call(this, middle); } return middle; } } // 测试 console.log(compose(a => a, b => b, (c,b) => c+b)('a', 'b'));</code></pre> <p>这个是简单的compose 函数. 下面,我们来看一下重点,关于 redux-middleware 的核心方法, applyMiddleware.</p> <h2><strong>applyMiddleware 中间件</strong></h2> <p>由于这个中间件有点复杂, 对传入的函数有具体的要求. 我们先来看一下使用该方法的上下文:</p> <p>直接看 offical website 找到一个 demo:</p> <pre> <code class="language-javascript">let store = createStore( todoApp, // applyMiddleware() tells createStore() how to handle middleware applyMiddleware(logger, crashReporter) )</code></pre> <p>最终 applyMiddleware return 的结果,还需要返回到 createStore 里去的. 通过 createStore 传入方法时, 函数里面并未对 里面做什么处理.</p> <pre> <code class="language-javascript">function createStore(reducer, preloadedState, enhancer) { // 这里就是一些列条件判断, 如果你使用 middle 是上面的形式,那么就会直接将参数赋给 enhancer if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } // 实际上调用 applyMiddleware 方法的地方. 注意他传入的参数即可. z return enhancer(createStore)(reducer, preloadedState) } }</code></pre> <p>ok, 我们了解了传入 applyMiddleware 的参数后, 继续看. 中间件的写法:</p> <pre> <code class="language-javascript">// 这里就看一下logger 就Ok const logger = store => next => action => { // debug info console.group(action.type) console.info('dispatching', action) let result = next(action) // debug info console.log('next state', store.getState()) console.groupEnd(action.type) return result } // 我们将 debug 信息去掉之后,可以得到一个精简版的 middleware const logger = store => next => action => { // 传递前, 执行的代码 let result = next(action) // 传递完, 执行的代码 return result }</code></pre> <p>看到这里,有种 koa 的感觉. next(action) 显示的将控制权交给下一个函数进行执行. 相当于就是 onion model.</p> <p>这里, 放一下 applyMiddleware 的源码:</p> <pre> <code class="language-javascript">export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer) var dispatch = store.dispatch var chain = [] var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }</code></pre> <p>看到这里, 我也是有点惊讶, 第一是他的中间件传递的复杂度巧妙的转化为箭头函数的写法, 第二是他显示的改变了真正的dispatch的内容。</p> <p>最后实际调用整个流程,是直接根据applyMiddleware提供的方法来的:</p> <pre> <code class="language-javascript">// 注意这里是 applyMiddleware 提供的 dispatch 方法 store.dispatch(action)</code></pre> <p>如果按照上面的调用方式写的话,具体调用顺序就是:</p> <pre> <code class="language-javascript">applyMiddleware(logger, crashReporter)</code></pre> <p style="text-align:center"><img src="https://simg.open-open.com/show/8b0d639ad77367aaf06b4addf128256a.png"></p> <h2><strong>applyMiddleware all procedure</strong></h2> <p>applyMiddleware 整个执行过程:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/bfbae1a1fd06c7e0ce287571bbcabc52.png"></p> <p>对应于上文, 整个API的流程图为:</p> <p>关键点在于applyMiddleware 和 中间件两个内容.</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/6d3d9c1cb777b8fd5dcd67918f0384a8.png"></p> <p>关于 redux-middleware 还有一个比较流行的库, 即, redux-thunk . 该库灰常简单, 就一个函数.</p> <h2><strong>redux-thunk</strong></h2> <p>直接看源码算了:</p> <pre> <code class="language-javascript">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;</code></pre> <p>他和原来的中间件的写法有一个非常不同的地方,在于. 他写中间件的地方, 不在 createStore 里面, 而在 dispatch 里面.</p> <pre> <code class="language-javascript">// 初始化调用 const store = createStore( rootReducer, applyMiddleware(thunk) ); // thunk 类型的中间件 function doSth(forPerson) { // 这里必须返回一个函数... 才能达到中间件的效果 return function (dispatch) { return async().then( sauce => dispatch(makeASandwich(forPerson, sauce)), error => dispatch(apologize('The Sandwich Shop', forPerson, error)) ); }; } // 更简便的写法可以为: let doSth = forPerson=>dispatch=> async().then( sauce => dispatch(makeASandwich(forPerson, sauce)), error => dispatch(apologize('The Sandwich Shop', forPerson, error)) )</code></pre> <p>不过,怎样用, 还是看自己吧. 本人比较倾向于使用 原来的 applyMiddleware. 因为, 那个已经满足我的需求了.</p> <p> </p> <p>来自:https://segmentfault.com/a/1190000006876228</p> <p> </p>
本文由用户 vb672616 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
转载本站原创文章,请注明出处,并保留原始链接、图片水印。
本站是一个以用户分享为主的开源技术平台,欢迎各类分享!