我在学习redux的时候,一开始也觉得很难,很多东西很难以理解,因为我是先简单学习了一遍Vue的,因为工作中用的都是react,所以Vue也忘得差不多了。写这篇文章的原因是想帮助想入门react的小伙伴们,同时也是对自己的一个技术小总结吧。
react难就难在它很自由,就提供了一个JSX,然后就没有了。不过和当初React的定位也有关系,它并不是一个框架,而是一个UI库。而Vue才是一个框架,因为很多事情它都帮你完成了,使用的时候仅需要知道相关的API。
tips:可以这么粗浅的理解react,UI = fn(data)
好了扯远了,下面开始带你入门redux吧。
redux
首先,在学习之前,要说明一点,react和redux没有任何关系,没有任何关系,没有任何关系;重要的事情说三遍。那redux是什么呢?是一个管理数据的仓库。
为什么需要redux呢?
原因是因为前端数据的复杂度没有人来管理,因为react当初的定位也是为了解决中大型项目,项目一旦复杂起来,需要共享的数据就会很庞大复杂。
redux的核心概念
在介绍核心概念之前,我们先来了解一个东西,就是Flux。在Flux中,Facebook最早提出了一个action的概念,通过action来直接修改数据仓库store,action是一个普通的对象,用于描述要干什么。store根据不同的action来对数据进行操作。
这样就会有一个问题,当业务复杂,数据庞大的时候,store的压力就很多了,所以才有了后面redux的出现,到这里,你只需要知道redux的三个核心概念就可以了。
action:相当于页面请求等操作,分发dispatch
store:数据的仓库
redux是如何工作的?
第一步:首先action是一个平面对象,就是一个简单的对象,必须要传type属性,类型任意,代表着你要干什么,还有一个可选属性是payload,就是请求时候的附加数据。
第二步:store接收到action的请求之后,把旧的数据state,和请求action交给reducer,让reducer来修改数据,修改完之后返回一个新的数据newState给我,以便更新数据。
明白了上图描述的redux工作流程之后,我们需要创建一个store,因为根据上图,不管是接收action,或者是触发reducer修改数据,都是根据数据仓库的,所有我们先要创建一个数据仓库。直接看下面代码和注释吧。
createStore
createStore 返回值是一个对象,对象包含以下几个属性
getState:得到当前的state数据
Symbol('observable'):提案,暂时用不到
tips:到这里,熟悉发布订阅模式的小伙伴,就知道其实 createStore 内部的代码原理就是使用了发布订阅模式。不熟悉发布订阅模式的小伙伴需要去了解一下。
代码
接着我们把action和reducer完善好
最后我们就可以使用了,结合上图的两个步骤理解
tips:最好自己手动实现一遍代码,然后按照redux工作原理两大步骤图理解一下。
bindActionCreators
根据上面redux的工作流程,有没有发现太麻烦了?的确是太麻烦了,尤其是第二步,要自己主动去使用store.dispatch,然后再往里面塞action的平面对象。那有没有方便一点的方法呢?redux提供了一个函数,bindActionCreators,增强action创建函数,让action之后自动触发dispatch。
代码
使用起来就相当简单了,下面做一个对比
combineReducers
现在还有一个问题,就是仓库数据只有一个,只有一个reducer,而在实际的项目中,数据是很庞大的,有很多个reducer,那怎么来管理这么多数据呢?
代码
我们可以先这样解决问题
其实redux很贴心,为我么提供了相当于上面效果的一个函数,就是 combineReducers。
完善 validiteReducers 函数
使用的时候,只需要简单的导出 combineReducers 的运行结果即可,因为它返回一个总的reducer函数,可以对比一下不使用combineReducers的时候。
middleware
现在我们基本上算是完成了一个简陋版的redux,但是在实际的业务开发中,往往需要请求数据,打印日志之类的很多附加功能,我们就举一个简单的例子。现在有一个需求,我要打印旧数据和新数据。首先我们分析一下需求,结合redux的工作流程,我们知道,action必须返回一个没有副作用的平面对象,reducer必须是一个纯函数。况且action只能拿到旧数据,reducer也不行,观察发现createStore里面的可以拿,那只能在里面做手脚了。怎么做手脚呢?又不能影响原来的功能。
代码
机智的我们可以先这样做
其实这个就是middleware中间件的原理。而且,中间件如何运用在redux上?总不能像上面这么low的写法吧?别急,我们一步步慢慢分析,理清楚下面这些概念
先看第五点,要使用中间件,必须调用applyMiddleware函数,将函数的返回值作为createStore的第二或第三参数,来告诉仓库,我们创建仓库的时候,要使用中间件。假设我们有下面三个中间件,创建仓库的时候调用applyMiddleware函数。applyMiddleware函数会倒着来运行每个中间件。在这里你可以先不管它为什么倒着来,先忽略。
然后用代码来写一遍,先书写三个中间件。
然后使用中间件
applyMiddleware
到这里,你可能会问applyMiddleware为什么要倒着来运行所有的中间件,这和代码的运行逻辑有关,下面我们就来解析一下applyMiddleware是怎么来把所有的中间件运行起来的。在介绍applyMiddleware前,我们先来认识一下compose,它是函数组合, 函数式编程中的声明式编程,其实就是把所有的函数的功能组合到一起。
compose
其实可以简单的理解成,把所有函数的功能组合成一个函数,就是compose。
有了compose之后,我们就可以实现applyMiddleware的原理了,如下代码
到这里,我们就知道为什么applyMiddleware一开始调用的时候是反过来的了。compose函数式编程的魔法,真香!!!
至此,三个中间件就会运用在创建函数里面了,这也是为什么创建函数的第二个参数可以传中间件或者默认值,createStore的完整代码应该是这样的。
只看上面的代码肯定很羞涩难懂,结合下面的图片看才好理解
其实redux用applyMiddleware使用中间件就是一个洋葱模型,用到切洋葱(redux)的时候真想哭(学redux),吃洋葱(react+redux)的时候,真香。
redux-thunk【扩展】
/**
代码
那为什么要放在第一个呢?
tips:再仔细想下整个流程,最后跟着思路码一边。
thunk源码
connect
前面我们都是在讲redux怎么管理数据,那我们的组件怎么使用数据呢?
假设我们都定义好了action、reducer,并且创建好了store。
那么接下来我们可以这样拿到数据(Users组件拿),直接定义两个方法,把需要的数据映射进去。
仔细看mapStateToProps、mapDispatchToProps。我们把需要的数据直接通过导入store,传进去就完事了。同时订阅一下this.setState就可以完成改变数据就刷新界面的效果。
redux还是很贴心的,哪里麻烦就出哪里的API,这时候就可以使用react-redux中的 connect 方法了。首先你要理解下面的笔记,或者可以直接去看相关的API文档。
有了connect之后代码就可以变得很简单了。直接导出就完事了
总结
redux其实使用不难,但是理解原理,解析原理代码是非常绕的,一定要多写几遍代码理解才能真的学会使用redux。
这里提供我学习redux源码的所有代码笔记,有兴趣的小伙伴可以clone下来研究学习。
github:github.com/huangruitia…