Redux前几天(2018.04.18)发布了新版本,6个commit被合并到master。从诞生到现在的4.0版本,Redux在使用层面一直保持着平稳的过渡。同时,不久前,React也从15版本升级到16版本,开发者无需做太多改动即可“无痛升级”。但是在版本迭代的背后有很多有趣的设计值得了解。Redux的这次升级也是如此。本文将从本次版本升级入手,从源码变化入手进行分析。通过下面的内容,相信读者可以对JavaScript的基础层面有更深入的了解。本文支持前端初学者的学习,更适合有阅读Redux源码经验的朋友。核心源码不再重复分析,更多关注升级和改动。变化概览本次升级有22处变化,大部分体现在TypeScript、CommonJS和ES构建的使用,状态错误抛出等方面。对于工程和配置的变化,我们不会再多说了。主要从代码细节入手,从基础入手,重点关注以下改动:中间件APIdispatch参数处理;应用中间件更改;bindActionCreators透明地处理这个;调度时,冻结状态;,我们直接进入主题。applyMiddleware参数处理这个变化是由Asvarox提出的。熟悉Redux源码中applyMiddleware.js设计的读者,一定对middlewareAPI不陌生:对于每一个中间件,它都可以感知store的一部分,即middlewareAPI。这里有一个简短的扩展:constmiddlewareAPI={getState:store.getState,dispatch:(action)=>dispatch(action)};chain=middlewares.map(middleware=>middleware(middlewareAPI));dispatch=compose(...chain)(store.dispatch)创建中间件存储:letnewStore=applyMiddleware(mid1,mid2,mid3,...)(createStore)(reducer,null);我们看到applyMiddleware是一个三层的curry函数。它会依次获取三个参数,第一个是middlewares数组,[mid1,mid2,mid3,...],第二个是Redux原生的createStore,最后一个是reducer;applyMiddleware使用createStore和reducer创建store,然后store的getState方法和dispatch方法直接和间接赋值给middlewareAPI变量。middlewares数组允许每个中间件通过map方法以参数middlewareAPI单独执行。执行后得到链数组,[f1,f2,...,fx,...,fn],然后组成链中的所有匿名函数,[f1,f2,...,fx,...,fn]组装成一个新的函数,也就是一个新的dispatch,当newdispatch执行时,[f1,f2,...,fx,...,fn]会从右到左依次执行。以上解释修改自:纯渲染专栏。好了,简单介绍完中间件机制,我们来看看这个变化。故事源于Asvarox设计了一个自定义的中间件,这个中间件接收到的dispatch需要两个参数。他的“杰作”是这样的:constmiddleware=({dispatch})=>next=>(actionCreator,args)=>dispatch(actionCreator(...args));对比传统中间件的写法:constmiddleware=store=>next=>action=>{...}我们可以很明显的看出他的写法有什么问题:基于原来的Redux源码,后面的argsactionCreator参数将丢失。所以他提出的修改是:constmiddlewareAPI={getState:store.getState,-dispatch:(action)=>dispatch(action)+dispatch:(...args)=>dispatch(...args)}ifIf好奇他为什么要这样设计自己的中间件,可以参考issue#2501。我个人认为对于需求,可以通过其他方式避免他的“奇葩”方法;但是对于Redux库来说,扩展middlewareAPI.dispatch参数确实更合适。让我们停止这种变化,停止挖掘死胡同。应该学习:基于ES6的不确定参数和传播运算符的魔法。虽然我一直在说,一直在提,但是我们在实际开发程序的时候还是要注意,养成良好的习惯。基于此,同样的变化也体现在:exportdefaultfunctionapplyMiddleware(...middlewares){-return(createStore)=>(reducer,preloadedState,enhancer)=>{-conststore=createStore(reducer,preloadedState,enhancer)+return(createStore)=>(...args)=>{+conststore=createStore(...args)letdispatch=store.dispatchletchain=[]此更改由jimbolla建议。bindActionCreators为此透明地处理了Redux中的bindActionCreators,达到了dispatchwrappingaction的目的。这样bindActionCreators创建的方法就可以直接调用dispatch(action)(隐式调用)。可能很多开发者不经常使用,这里稍微扩展一下。在action.js文件中,我们定义了两个actioncreators:直接使用:import{bindActionCreators}from'redux';import*asoldActionCreatorfrom'./action.js'classC1extendsComponent{constructor(props){super(props);const{dispatch}=道具;this.boundActionCreators=bindActionCreators(oldActionCreator,dispatch);}componentDidMount(){//由react-redux注入的dispatch:let{dispatch}=this.props;letaction=TodoActionCreators.addTodo('使用Redux');派遣(行动);}render(){//...让{dispatch}=this.props;让newAction=bindActionCreators(oldActionCreator,dispatch)返回
