摘要:上一篇文章讲解了如何使用,本篇文章将进一步深入,从的源码入手,深入学习的中间件机制。的功能是让支持异步,让我们可以在中跟服务器进行交互等操作,而他的实现。。。
上一篇文章讲解了redux如何使用,本篇文章将进一步深入,从redux的源码入手,深入学习redux的中间件机制。
在这里我们会以一个redux-thunk中间件为例,逐步分解redux的中间机制如何操作,如何执行。
闲话不多说,上代码。
如何加载中间件import { createStore, applyMiddleware } from "redux"; import thunk from "redux-thunk"; import rootReducer from "./reducers/index"; // create a store that has redux-thunk middleware enabled const createStoreWithMiddleware = applyMiddleware( thunk )(createStore); const store = createStoreWithMiddleware(rootReducer);
这里需要用到redux中提供的一个工具方法,叫做applyMiddleware,向该方法传入你想要使用的中间件,完了之后再传入createStore方法,
最终形成新的创建store的方法。
这显然是一个装饰器模式,通过不同的中间件对createStore方法进行修饰,最后形成新的createStore方法,那么创建的store就具有这些中间件的特性,
非常出色的设计,惊喜不仅在这,看了之后的代码你就更不得不佩服作者的代码设计能力。
瞬间觉得别人都是码神,而我就是码农有木有/(ㄒoㄒ)/~~
中间件加载机制的实现先来看applyMiddleware方法的实现
import compose from "./compose"; /** * Creates a store enhancer that applies middleware to the dispatch method * of the Redux store. This is handy for a variety of tasks, such as expressing * asynchronous actions in a concise manner, or logging every action payload. * * See `redux-thunk` package as an example of the Redux middleware. * * Because middleware is potentially asynchronous, this should be the first * store enhancer in the composition chain. * * Note that each middleware will be given the `dispatch` and `getState` functions * as named arguments. * * @param {...Function} middlewares The middleware chain to be applied. * @returns {Function} A store enhancer applying the middleware. */ export default function applyMiddleware(...middlewares) { return (next) => (reducer, initialState) => { var store = next(reducer, initialState); 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 }; }; }
这就是redux里面这个方法的源码,其中还一半是注释有木有。。。本来以为肯定有百来行代码的
当然这里不得不说es6的特性提供了非常多的帮助,所以为了省力吧es6玩透还是灰常有必要的(更别说为了装X了(^__^) )
从这里开始代码就有点绕了,我们逐行分析
return (next) => (reducer, initialState) => {...}
整个applyMiddleware方法就是返回了一个方法,根据applyMiddleware方法的使用,我们可以知道next就是createStore方法,
因为最终我们要返回的是一个装饰过的createStore方法,那么接收的参数肯定是不会变,所以最终我们调用createStoreWithMiddleware方法其实就是调用
function (reducer, initialState) { var store = next(reducer, initialState); // next即为最初的createStore方法 // ...以下省略 }
var store = next(reducer, initialState); var dispatch = store.dispatch; var chain = [];
这里没什么好讲的,首先创建了一个store,这个store就是最原始的通过createStore创建的store,后两行只是变量赋值
var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) }; chain = middlewares.map(middleware => middleware(middlewareAPI)); dispatch = compose(...chain)(store.dispatch);
这里是关键,必须详细进行讲解。
首先,这边声明了一个middlewareAPI对象,这个对象包含两个方法:
getState:store中的getState方法的引用
dispatch:对本身的dispatch方法进行一次封装
然后
chain = middlewares.map(middleware => middleware(middlewareAPI));
我们来仔细看看这行代码,首先我们对所有的中间件进行一个map,map结果就是调用中间件方法,将middlewareAPI作为参数传入,
这里我们拿redux-thunk中间件举例,来看看一个中间件是长什么样子的,传入的参数又是用来干嘛的。
export default function thunkMiddleware({ dispatch, getState }) { return next => action => typeof action === "function" ? action(dispatch, getState) : next(action); }
redux-thunk的功能是让action支持异步,让我们可以在action中跟服务器进行交互等操作,而他的实现。。。(⊙﹏⊙)b是的,又是这么几行代码。
我们回顾之前的代码,在map所有中间件的时候我们调用了thunkMiddleware方法,传入两个方法dispatch和getState,然后返回了一个方法,
我们大致抽象一下,应该如下:
function (next) { return function (action) { typeof action === "function" ? action(dispatch, getState) : next(action) } }
于是我们接下去分析applyMiddleware里面的代码,
chain = middlewares.map(middleware => middleware(middlewareAPI));
现在我们知道chain是一个数组,每一项是调用每个中间件之后的返回函数
dispatch = compose(...chain)(store.dispatch);
compose是redux里面的一个帮助函数,代码如下:
export default function compose(...funcs) { return arg => funcs.reduceRight((composed, f) => f(composed), arg); }
~~(>_<)~~我已经不想再吐槽什么了,
我们看到这边先调用了compose函数,传入了结构后的chain数组,然后compose函数返回的也是一个函数:
function (arg) { return funcs.reduceRight((composed, f) => f(composed), arg); // funcs就是中间件数组 }
然后我们把store.dispatch函数作为arg传入这个结果,这里reduceRight可以参考这里
。那么这边得到的结果是什么呢?
// 假设中间件数组是[A, B, C] // 那么结果就是A(B(C(store.dispatch)))
再次结合redux-thunk来看,我们假设只有一个中间件,那么最终的dispatch方法就是
function (action) { typeof action === "function" ? action(dispatch, getState) : next(action) } // 这里的next方法,就是真正的store.dispatch方法 // 这里的dispatch是(action) => store.dispatch(action)
我们再结合redux-thunk的使用方法来分析一下,
function incrementAsync() { return dispatch => { setTimeout(() => { // Yay! Can invoke sync or async actions with `dispatch` dispatch(increment()); }, 1000); }; }
这是使用redux-thunk时可以定义的异步action,我们触发action的时候调用的是
dispatch(incrementAsync())
incrementAsync返回的是
function (dispatch) { setTimeout(() => { // Yay! Can invoke sync or async actions with `dispatch` dispatch(increment()); }, 1000); }
这个时候我们回想经过中间件加工的dispatch方法:
function (action) { typeof action === "function" ? action(dispatch, getState) : next(action) } // 这里的next方法,就是真正的store.dispatch方法 // 这里的dispatch是(action) => store.dispatch(action)
action是一个函数,所以action === "function" ?成立,那么就执行action, 并把中间件接收到的dispatch方法((action) => store.dispatch(action))方法作为参数传入,在异步方法执行完之后再次触发真正的action。如果action不是异步的,那么久直接返回一个对象,这个时候action === "function" ?不成立,就直接调用next,也就是原始的store.dispatch方法。
我们再接着想,如果我们有许多个中间件,那么没一个中间件的next就是下一个中间件直到最后一个中间件调用store.dispatch为止。
以上的代码非常绕,建议去专研一下源码。这么精简的代码包含了非常多的函数式编程的思想,也用到了装饰器模式的原理,不得不说:
太烧脑啦/(ㄒoㄒ)/~~
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/78911.html
摘要:因为工作中一直在使用,也一直以来想总结一下自己关于的一些知识经验。于是把一些想法慢慢整理书写下来,做成一本开源免费专业简单的入门级别的小书,提供给社区。本书的后续可能会做成视频版本,敬请期待。本作品采用署名禁止演绎国际许可协议进行许可 React.js 小书 本文作者:胡子大哈本文原文:React.js 小书 转载请注明出处,保留原文链接以及作者信息 在线阅读:http://huzi...
摘要:在项目中用好高阶组件,可以显著提高代码质量。高阶组件的定义类比于高阶函数的定义。高阶函数接收函数作为参数,并且返回值也是一个函数。 React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。 1. 基本概念 高阶组件是React 中一个很重要且比较复杂的概念,高阶组件在很多第三方库(如Redux)中都...
摘要:使用上一篇文章的例子来说明下自由变量进阶期深入浅出图解作用域链和闭包访问外部的今天是今天是其中既不是参数,也不是局部变量,所以是自由变量。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第二期,本周的主题是作用域闭包,今天是第7天。 本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进阶计...
摘要:特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 特意对前端学习资源做一个汇总,方便自己学习查阅参考,和好友们共同进步。 本以为自己收藏的站点多,可以很快搞定,没想到一入汇总深似海。还有很多不足&遗漏的地方,欢迎补充。有错误的地方,还请斧正... 托管: welcome to git,欢迎交流,感谢star 有好友反应和斧正,会及时更新,平时业务工作时也会不定期更...
阅读 2956·2021-09-08 10:43
阅读 1006·2019-08-30 15:53
阅读 920·2019-08-30 13:51
阅读 804·2019-08-29 14:03
阅读 770·2019-08-26 18:35
阅读 1205·2019-08-26 13:38
阅读 1542·2019-08-26 10:34
阅读 3450·2019-08-26 10:21