Redux学习笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lihei12345/article/details/50256913

随着JavaScript单页面应用开发的日趋复杂,JavaScript需要管理比之前任何时候都要多的State。State可以包括服务器返回的数据,本地缓存的数据,也可以是本地创建还没有发送给服务器的数据。UI state也变得日趋复杂。

管理持续不断变化的state变得非常困难,最终的结果就是对state的失控,你不知道state是when, why 以及how发生变化的。导致的问题就是,当系统变得错综复杂时,重现bug或者添加新功能都将变得无比困难。

这个问题之所以变得难以处理,是因为我们混淆了两个我们人类比较难理解的概念:mutation 和 asynchronicity。这两个概念分开是非常不错的概念,但是它们混在一起的时候会变得一团糟。类似React这样的类库就是为了解决这类问题而出现的,它们通过移除异步和直接的DOM操作来解决这个问题。但是管理 state of your data却是开发者的任务。而这就是Redux起作用的地方。

Redux 三个原则
  1. Single source of truth: The state of your whole application is stored in an object tree within a single store.
  2. State is read-only: The only way to mutate the state is to emit an action, an object describing what happened.
  3. Mutations are written as pure functions: To specify how the state tree is transformed by actions, you write pure reducers.


Flux vs Redux


Redux既可以认为是Flux的实现,也可以认为不是。

Redux从Flux中借鉴了几个非常重要的特性, Redux和Flux的相同点:
  • Redux规定你必须将model update logic集中到应用特定的layer上(Flux中是”stores”,Redux中是”reducers”)。
  • Redux和Flux都不允许直接修改data,而是必须通过叫做”action”的plain object来描述对data的修改。

Redux和Flux的不同点:
  • Redux没有Flux中Dispatcher的概念。Redux使用 pure functions来代替Redux中的event emitter,而pure functions非常容易管理,所以并不需要类似Dispatcher这样额外的实体来管理他们。看待的角度不同的话,这种实现方式跟Flux有差异也可能认为仅仅是实现细节不一致。而Flux经常被描述为 (state, action) => state。从这个概念来说的话,Redux的确是Flux架构的一种,只是得益于pure functions而变得更加简单而已。
  • Redux跟Flux另外一个重要的区别是,Redux假设你永远不会直接mutate your data。在state中使用plain objects和arrays都是允许的,但是在reducers中mutating它们是不被允许的。正确的做法应该是返回一个新的对象给state替换相应的字段,这个新对象可以很容易实现,通过使用babel支持的ES7的 object spread syntax或者类似immutable这样库。

from:  http://staltz.com/unidirectional-user-interface-architectures.html,从一个老外网站上找来的示意图,关于Flux与Redux的对比

Flux的结构图:


Redux的结构图:





Actions


Actions 是用来从 application向 store 中传递data的信息负荷单元(payloads of information)。需要注意的是,Actions 是store的唯一信息来源,可以通过store.dispatch() 来向store传递Actions。

Action Creators 是用来创建actions的函数。在传统的Flux的实现中,action creators在被调用的时候就会触发一个dispatch,如下:

但是在Redux中,action creators 没有任何side-effects的pure functions,它们仅仅是返回一个action:




Reducers


Actions 用来描述发生了什么fact,Reducers 则是指明如何根据这些Actions来更新application的state。在具体写Reducers之前,我们首先需要设计application的state的结构。在Redux中,“ all application state is stored as a single object”。这种结构要求我们在开始app的开发之前,必须首先勾画出application的state。

当我们设计application的state的时,如果开发的app结构比较复杂,需要不同的entities来相互引用,这时我们建议尽量把state变得normalized,不要任何嵌套。详细可以参考normalizr的文档: https://github.com/gaearon/normalizr

当我们确定了state的结构了之后,我们就可以开始写Reducer,Reducer是一个pure function,接收之前的state和一个action,然后返回下一个新的state:(previousState, action) => newState。在开发Reducer的时候,有一点非常重要,那就是 reducer stays pure,所以永远不要在reducer中做下面这些使用:
  • Mutate its arguments;
  • Perform side effects like API calls and routing transitions;
  • Call non-pure functions, e.g. Date.now or Math.random();

Pure的含义: 传入同样的参数,计算出next state,然后返回这个state。No surprises. No side effects. No API calls. No mutations. 仅仅是一个计算的过程。

关于Reducers的详细讲解,可以参考官方文档, http://rackt.org/redux/docs/basics/Reducers.html,文档中详细介绍了处理actions到splitting reducers,到reducer composition(the fundamental pattern of building Redux apps),最后redux提供的combineReducers()方法,通过这个介绍,可以详细理解Redux实现的核心方法和思路。

splitting reducers的思路是每个reducers只管理global state中对应的那一部分数据,所以reducer的参数中的state在每个reducer中都是不同的,跟它管理的那部分state有关系。例如下面的todoApp方法,返回的state是global state,而这个global state中的visibilityFilter部分数据和todos部分数据,则分别由visibilityFilter()和todos()这两个reducer来处理,而todoApp将这两个reducer处理返回的结果进行合并,就是global state,然后再把这个global state返回,就完成了reducer的处理过程。


而combineReducers() 是redux提供的一个工具,它做的事情就是生成一个function,这个function做的事情就是上面todoApp做的事情。调用不同的reducers,将global state中reducer对应的slices of state作为参数传入到这些reducers,然后把这些reducers返回的结果重新合并为一个single object。其实没什么神奇的。其中如何确定reducer对应的是哪一部分slices of state呢,就是通过类似上面的todos和visibilityFilters这些key值。

上面这一段翻译比较拗口,所以附上原文:


例如下面,doSomethingWithA传入的参数就是state.a,这个就是key值 a 来决定的:

而对于上面todos那个例子,key和reducer的name一致,则直接可以简写为:



Store


Actions描述了关于“what happened”的事实(fact),Reducers则根据这些actions来更新state,而Store则是Actions和Reducers连接在一起的对象,Store有以下的职责:
  • Holds application state;
  • Allows access to state via getState();
  • Allows state to be updated via dispatch(action);
  • Registers listeners via subscribe(listeners);
需要再次强调一下,Redux只有一个store,这个跟Flux不一样,Redux使用reducer composition来代替多个stores。我们可以利用前面讲到的combineReducers生成的reducer,传入到createStore()方法中来生成一个store,例如:




Date Flow


Redux架构始终是在围绕着”strict unidirectional data flow”这个概念的。 这个概念意味着application中所有的data都遵循同一个lifecycle pattern,这使得我们app的变得更predictable并且提高代码的可读性。Redux还推崇将data进行normalization( https://github.com/gaearon/normalizr),这样就不会出现那种同一份数据有多份独立的拷贝,并且这些拷贝相互无法引用的情况。

Redux中的data的lifecycle有4步:
  1. You call store.dispatch(action).
  2. The Redux store calls reducer function you gave it.
  3. The root reducer may combine the output of multiple reducers into a single tree.
  4. The Redux store saves the complete state tree returned by the root reducer.



Usage with React


我们首先要明白的一个概念是 Redux 跟 React 是没有任何联系的。Redux支持跟React , Angular, Ember, jQuery,  甚至 vanilla Javascript结合在一起使用。但是,Redux和React/Deku这类框架一起使用却能达到更佳的效果。因为这类框架让我们能够使用 state 函数的形式来直接描述UI,Redux就只需要更新state就能响应用户发出的actions,而不用再去考虑update UI的问题。

Redux和React绑定在一起使用的时候借鉴了 “separating container and presentational components”这个概念( https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0)。这个概念中,只有 top-level components 才知道Redux的存在,而它下面的components应该只用来展示并且只能通过props来接收data,并且通过props来传递callback function的形式来发送actions,Presentational Component不能直接来dispatch actions。




Connecting to Redux


我们使用Redux官方提供的 react-redux库( https://github.com/rackt/react-redux)来将react和redux衔接在一起,这只需要几步操作即可。

第一步,react-redux提供了Provider标签,我们需要在rendering之前将 root component 包含在<Provider>标签之内,Provider有个属性store把我们创建的store传入,用法如下:

上面这段代码,使得<Provider>标签下面的Components都能访问到store instance。(内部是通过React的“context”特性来实现的)。

第二步,我们需要使用react-redux提供的connect()函数,将那些我们想要跟Redux进行连接的components包含起来。任意的component通过connect()调用包含之后,这个component的props中都会包含一个dispatch函数和它需要的任意state(global state中的一部分经过变换而来)。

其中,dispatch function的获取很简单,我们在component内部只需要直接使用this.props.dispatch就能拿到一个用来dispatch actions的function。而对于component所需要的state数据,则需要一个额外的selector function作为connect的参数传入,这个selector function的参数是global Redux store’s state,它会把这个global state进行变换,处理成component所需要的数据返回。看下面的代码:




Async Actions


异步的Actions的行为跟同步区别不是很大,只是通过一些Middleware中间件,并且结合promise这类第三方库来解决了这个问题。一般的异步操作基本上来说就是网络请求,这个工作是放在Action creators中完成,这点需要注意,Reducer应该是Pure functions,不应该处理这些。

可以使用一些类库来简化这个步骤: https://github.com/gaearon/redux-thunk 或者  https://github.com/acdlite/redux-promise



总结


本来是要学习RN的,结果在学习的过程中,发现RN的技术体系非常复杂,学着学着就搞了很多,Redux是我的学习栈里面最后一项了。看了很多RN方面的实现,但是感觉不结合Flux这种单向数据流的情况下,RN的强大无法完全体现出来,后面准备用RN + Redux 实现一个知乎日报。

最后附上一幅结构图,在上面的结构图中,没有反应Containers components和Presentational components的区别,以及UI事件如何变成actions,下面我自己绘制了一幅图更加详细一些:




参考

猜你喜欢

转载自blog.csdn.net/lihei12345/article/details/50256913