学习Redux刷新了我的前端App状态管理观

写在前面的话

我听过Redux好久了,不过真的开始学大概在一个月前,学会Redux之后我用一周多的时间重构了之前纯React写的一个小项目。学习的过程中心态变化很大,但是最终真的感觉如果再写复杂一点的项目,我应该再也不会直接用state管理所有状态了。Redux真多刷新了我的状态管理观。

本文的写在目的在于让那些一直在使用React,但是还没有使用Redux管理复杂应用状态的同学了解Redux的核心思想并能够更加平滑的学习使用Redux。

为什么要使用Redux

快双十一了,突然想到一个比喻来解释为什么会有React这类框架的存在,为什么我们要使用Redux,以前使用jQuery不也挺快乐的嘛。

一个比喻

想想现代物流系统的发展的不同阶段,我们寄一件东西的过程,

  • 没有物流系统时:

    • 打包准备好要送出去的东西;
    • 出门乘车到达朋友家,把东西送给朋友;
    • 很直接很方便,很费时间,送东西的距离有限(同城?);
  • 出现了各大物流公司时:

    • 打包准备好要送出去的东西;
    • 出门到距离家最近的物流公司,填写物品,收件人等基本信息;
    • 物流公司替你送物品到你的朋友处,自己可以回家做别的事情了;
    • 多了一个中介,要付一定的运送成本,但是东西可以送到远在几千公里之外的其它人了,用一点点钱节约了自己宝贵的时间,完成了以前我们不可能做的事情;

没错,没有物流系统时对应的就是以前使用jQuery的前端阶段,做一件事情很直接,如果只是送快递给一个特别近的人,那当然是非常方便。就算你要从北京送一个东西到上海,在这个阶段你也可以实现,但是需要付出的代价就太大了。

而出现了各大物流公司对应的就是使用React等框架的阶段,学习React需要付出成本,如果你只需要做简单的事情(比如说你想把东西送给你的邻居),那使用此类框架其实显得特别累赘,把简单的事情复杂化了,但是如果你想做复杂一点的事情(同时寄礼物给三个不同城市的人),那么选择快递公司是不会错的。

那么React和Redux的关系又该如何理解呢?

我觉得我们可以把React比如为一家自建快递系统的公司(京东?),在一定范围内,它的快递系统已经足够好了(部分一二线城市一天内直达),但是要管理这个日益复杂的快递系统,需要公司付出巨大的成本。

Redux可以看做业内最好的第三方快递系统(顺丰?),使用它比较贵(学习成本稍微有点高),但是他到达全国主要城市都会特别快,如果你是一个电商老板,采用这个第三方物流系统之后,你只需要关注于自己的货物,几乎不用再去关心物流怎么办了。

不同于顺丰每一次寄货都要那么高的价格,redux是一个学习一次,就可以免费寄货的优秀第三方快递系统,那我们当然要解散自己的物流公司来采用这个第三方的选择的。不过话说回来,学习是需要有成本的,所以是直接使用React还是学习Redux再使用,这是你一个你需要依据你自己项目的实际情况作出选择的事情。

不过这个例子可能并不足够合适,Redux做的不仅仅是管理原来React里state里面的状态。Redux其实可以接管我们的app里所有的数据。接下来我们具体看看Redux究竟做了什么。

Redux可以做什么

我们常常听到一种说法,“Redux是一个非常好的状态管理器”,那究竟什么是状态呢。

重新理解前端中的状态

想想我们平时看的网页,app,或者任何其它和我们有交互的东西,我们感觉到交互的发生是因为界面依据我们的行为作出了反馈,界面所有的改变,其实都可以看作是状态的改变,或者说界面会改变是因为我们的某个行为(事件)(click,drag,move...)触发了某个函数,函数造成了状态的改变,进而改变了界面。

如此看来,无论是显示隐藏这种可见的状态,还是从服务器获取更多的数据,这些都可以看做是状态,而这些状态就是我们的Redux要管理的。

换一句可能比较专业一点的说法吧,状态包括

  • API State;(数据)
  • UI State;(UI的表现形式)

前端状态管理史

为了更好的展示Redux的好,我们回顾一下前端的状态管理史(称为史其实并不合适,以下三种模式现在都有大量人在使用)。

  • 中古

    • jQuery 时期
    • 我们使用诸如$(element).addClass('active')这样的语句来改变状态;
    • 对简单的应用来说,这样写简单明了,但是状态一多一复杂就乱了,并不存在一个专门管理状态的地方;
  • 近代

    • React内部管理状态时期
    • 我们引入了state来管理组件状态,界面想显示不同的样子,我们通过各种函数来改变state来实现
    • 已经存在一个专门管理状态的state(对象,数组)了,对大部分应用来说,用state来管理状态已经足够了,但是应用复杂了会使得状态
  • 现代

    • 引入Redux等状态管理机制时期;
    • redux使用一个store来全局管理各种状态,提供一些不算复杂的api来专门管理状态;
    • 可以管理更加复杂的状态,通过redux的管理,状态的改变变得更加清晰,可预测,redux中的状态是一个只读属性,通过一定的方法,可以回到已经经历过的某个状态(时间旅行);

(这里没有说到MVC等机制有兴趣的同学可以看这篇文章做进一步对比了解,基本观点是MVC固然很好,但是配合React使用时,性价比不是那么高了。)

Redux是怎么管理状态的

上面已经说了Redux管理状态怎么好 怎么好,那Redux究竟是如何管理状态的呢?状态分为UI State 和 API State,Redux针对这两部分也提供了两种方法

  • 为改变已有的状态提供了方案;
  • 为异步获取新的数据提供了方案;

Redux的数据流

还是用图片来说明更加清楚
下图说明了Redux和React的状态流分别是怎么样的;


React和Redux分别是怎么管理状态的

下图说明了使用Redux管理状态为什么是可预测的


可预测的Redux状态

Redux的数据是如何流动的其实也是理解Redux的好处的关键部分之一,简单来说每个事件会发送一个action,action通过dispatch触发reduce,直接依据旧的state生成一个新state替代最顶层的store里面原有的state。

有一篇文章以漫画的形式把这个讲的特别透A Cartoon intro to redux

说了这么多使用redux管理状态的好处,但是你看到这里可能依旧不知道如何使用redux,不要着急,我和你分享我的Redux学习经验。

开始学习Redux

学习Redux前需要了解的基础知识

如果你已经能很熟练的使用React,我觉得学习Redux需要了解的基础知识,你应该都已经了解了。具体说来主要有以下内容;

  • React(Redux是flux架构的实现,虽然其也可以配合其它框架使用,但是它和React可能还是更配一些吧);
  • 基础的ES6知识(Redux重视函数式编程,会使得编程的结构看起来更加简洁);
  • 用了ES6(甚至ES7)当然免不了要学习使用Webpack,Babel等;
  • 还有一点,我觉得学习编程应该不怕折腾,使用Redux管理一个状态可能需要改好几个文件里的代码才能实现,编程不再显得那么直接(比如 通过connect.js调用action.js里的某个action,并依据这个action触发reducer.js里面的某个reducer函数依据现有的state,创建一个新的state),redux把一些操作给抽象化了,如果思维没有跟着改变,会让人有一种redux文档里面的东西我都看懂了,但是我怎么就是编不出来呢?那种痛苦的感觉。

学习使用Redux

网上关于Redux的教程特别多了(官方文档写的特别好),学习新概念是比较恼人的一个过程,所以我还是会对Redux提供的api做一个简洁的描述,然后我会把我这段时间看过的我觉得比较好的文章的链接放在下面以供大家参考。

Redux中的一些概念

  • state:app中的状态存放的地方,并且state是只读的,不同于React,Redux中state的更改,其实是创建了一个全新的state;
  • action:是一个对象,作用和他的名字一样,用来表明,你想要做的那件事情,该对象的属性type,用来标记,你要做的事情;
  • reducer:是一个函数,接收当前state,和一个action作为参数,依据action基于当前的state生成新的state;
  • dispatch:推送某个actionreducer;
  • action creater:一个创建action的函数,返回一个action对象;
  • 异步action:返回一个函数,和中间件配合可以很容易的实现异步操作;
  • store:可以理解为state的家,全局只有一个,有以下方法
    • getState():获取当前的state树;
    • dispatch(action):触发一个action,创建state;
    • subscribe(listener):
    • replaceReducer(nextReducer)
  • combineRedecers(reducers):当我们的应用比较复杂的时候,我们可能会分开写好几个reducer,这个函数的作用就是把这些单独的reduce合并为一个大的reduce,需要注意的是我们的state的结构和我们的各个reducer是一一对应的。
  • applyMiddleware(...middlewares):告诉redux我们会用到那些中间件,比如说要用到基础的异步,我们会用到thunk中间件;
let store = createStore(
    comReducer,
    applyMiddleware(thunk)
);
  • bindActionCreators(actionCreators, dispatch):绑定actionCreatordispatch以供直接使用;

配合 React 使用Redux需要掌握的一些概念

redux只是管理状态的一种方法,真的用在React里,使用作者提供的一个工具react-redux会更加方便,其api很简单,主要有以下几个;

  • <Provider store>,嵌套在React组件的最外层,因此可以把state传给所有的组件(利用了React的context);
  • 把React组件分为容器组件和UI组件两类,容器组件管理逻辑,UI组件管理显示效果二者通过connect方法连接,容器组件一般由UI组件依据connect生成;
  • mapStateToProps(),存在于容器组件中,针对UI组件的各状态(依据state,或者父组件的props)生成;
  • mapDispatchToProps(),存在于容器组件中,针对UI组件中的各可能改变state的事件定义的一系列的函数,依据props传给UI组件;

Redux参考文献

官方文档(中文)

What the Flux?! Let’s Redux.

a-cartoon-intro-to-redux

官网推荐的经典文章列表

阮一峰的三篇教程

Redux还有一个非常酷的工具,让我们可以实时看到当前的state,使用redux不可不用啊。
酷酷的工具redux devtools

还是得靠实践

在弄懂了一些关键的核心概念以后,如果还是不知道怎么写,就模仿官方给的多个例子针对自己的需求敲写一次吧。模仿了两个例子就肯定明白了。

后言

Redux也有自己的小生态,理解的一些技术辅助Redux更加方便的实现状态管理,其中有一些是下一步我特别想了解的比如说immutable-jsreselectnormalizr,当然因为学习了Redux,所以我也想对函数式编程做进一步理解,之前找到了一本非常好的关于函数式编程的书mostly-adequate-guide,分享给大家,大家一起学习。

推荐阅读更多精彩内容