这段时间研究了下Redux,写写自己对它的感觉

#1

原地址

这段时间看了下Redux,写自己对它的感觉。

先说下传统的redux

个人感觉,redux简化了flux的流程。

flux的流程是:

  1. view触发action中的方法;
  2. action发送dispatch;
  3. store接收新的数据进行合并,触发View中绑定在store上的方法;
  4. 通过修改局部state,改变局部view。

redux是:

  1. view直接触发dispatch;
  2. 将action发送到reducer中后,根节点上会更新props,改变全局view。

redux将view和store的绑定从手动编码中提取出来,放在了统一规范放在了自己的体系中。

而在基本的redux流程中,action只是充当了一个类似于topic之类的角色,reducer会根据这个topic确定需要如何返回新的数据;数据的结构处理也从store中移到了reducer中。

redux数据流如下图:

redux的基本原理实际上就是围绕着store进行的。

这个store不是flux中的store,而是通过createStore方法创建的;
createStore方法接收reducer函数初始化的数据(currentState),并将这2个参数并保存在store中。

createStore时传入的reducer方法会在store的dispatch被调用的时候,被调用,接收store中的state和action,根据业务逻辑修改store中的state;

store中包含subscribedispatchgetStatereplaceReducer这4个方法。相关API

其中,subscribedispatch顾名思义就是订阅和发布的功能;
subscribe接收一个回调(listener),当dispatch触发时,执行reducer函数去修改当前数据(currentState),并执行subscribe传入的回调函数(listener);

getState是获取当前store的state(currentState);

至于replaceReducer方法,就是动态替换reducer函数,这个我觉得应该算用的比较少吧。

说说Middleware

redux中的middleware还是比较简单的,它只是针对于dispatch方法做了middleware处理;也就是说,只接受一个action对象;使用也很简单,如文档中的例子:

const createStoreWithMiddleware = applyMiddleware(
  thunkMiddleware,
  loggerMiddleware
)(createStore);
store = createStoreWithMiddleware(rootReducer, initialState);

redux的middleware用reduceRight方法,将applyMiddleware方法中的参数串起来,原始的dispatch方法会最后执行。
如下图:

** 编写middleware **
如果需要自定义middleware也很简单,这个middleware只接收一个action,执行后也需要返回一个action;如果需要执行下一步,调用next(action)即可。

再来说说react-redux

react-redux,是对redux流程的一种封装,使其可以适配与react的代码结构。

react-redux首先提供了一个Provider,可以将从createStore返回的store放入context中,使子集可以获取到store并进行操作;

<Provider store={store}>
	{() => <App />}
</Provider>

其次react-redux提供了connect方法,将原始根节点包装在Connect下,在Connect中的state存储不可变对象,并将state对象中的props和store中的dispatch函数传递给原始根节点;

Connect在componentDidMount中,给store添加listener方法(handleChange),每当store中的dispatch被调用时执行handleChangehandleChange会去修改state中的porps,使原始根节点重新render;并且Connect已经在shouldComponentUpdate实现了PureRender功能。

handleChange更新state中的props逻辑主要由3个函数构成,这3个函数都由connect方法传入;

connect(mapStateToProps, mapDispatchToProps, mergeToProps)(App);

第一个函数接收store中state和props,使页面可以根据当前的store中state和props返回新的stateProps;
第二个函数接收store中的dispatch和props,使页面可以复写dispatch方法,返回新的dispatchProps
第三个函数接收前2个函数生成的statePropsdispatchProps,在加上原始的props,合并成新的props,并传给原始根节点的props。
相关API

在react-redux中的流程如下图:

1.View触发dispatch
2.进入reducer,修改store中的state
3.将新的state和props传入handleChange中,生成更符合页面的props
4.传给原始根节点重新render

最后的再说两句

redux的优点:

  1. redux把流程规范了,统一渲染根节点虽然对代码管理上规范了一些,只要有需要显示数据的组件,当相关数据更新时都会自动进行更新。

  2. 减少手动编码量,提高编码效率。

redux的缺点:

  1. 一个组件所需要的数据,必须由父组件传过来,而不能像flux中直接从store取。

  2. 当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新render,可能会有效率影响,或者需要写复杂的shouldComponentUpdate进行判断。

redux的疑问:

  1. redux如何处理多个api同时请求,在都得到结果后,再进行渲染(每个api都可能会单独拿出来做其他的请求)?

  2. 实际上在flux的流程中,在action中做api请求,然后返回数据,我并没有觉得违和。
    但是放在了redux中,通过redux-thunkredux-promise来阻止第一次dispatch,并将dispatch函数传入action中的回调方法再次调用。
    我感觉不是很符合正常的语言逻辑,使action的功能好像并不单一,是不是有什么更改的实现方式呢?

  3. 多个reducer文件怎样组合会更好些?是每种类型的数据放在每个文件里,与flux中的store类似,最后在一个总的reducer文件中做combineReducers

14 Likes
#2

厉害呀!

#4

寫得很好,那兩個圖非常直觀。

關於你提到的第一個缺點,其實我覺得應該是當做優點來看待的。Redux 其實更加注重數據的單一流向性,所有的 Component 都能變成沒有 states 的 Dumb Component,這一點反而是 Flux 沒有完全做到的。因此 Redux 能做到很多 Flux 做不到的事情,比如保留 store 的歷史記錄、回放、修改、與後端進行完全同步等等。

關於你說的第二個缺點,我在開發環境下用到 redux-form 加上 redux-loggerredux-devtools 這些 Middlewares 的時候,很明顯的感受到了卡頓。所以我是認同你這個觀點的,使用 Redux 這種狀態機模型的狀態管理方式確實會降低效率。不過我覺得這個是可以改善的。我能想到的解決辦法就是使用 Immutable.js 儲存 store 裡面的數據,在每個大的組件的 componentShouldUpdate 處做一個類似 Caching 的檢查,阻止沒有數據變化的組件樹的更新。但是這樣做的話就得要求盡量少用 react-redux 的 connect 功能,因為這個東西其實等同於 Flux 的單組件監聽的效果,所以進行了 connect 的組件,它的 parent component 是不知道它得到了哪些數據的,所以就沒法進行整個 DOM Tree 的 Caching。

關於你最後留下的思考:

####1 & 2. 用 Middleware 處理異步 API 請求,支持同時多個請求

一般用一個 Middleware 處理所有 Promise,轉換成幾個中間狀態的 Actions。
實現可以參照這個:APIClient.js

使用的時候只需要構造一個帶 promise(client) -> Promisetypes[REQUEST, SUCCESS, FAILURE] 字段的 Action 觸發即可。

{
  promise(client) {
    return Promise.all([
      client.get('method_1'),
      client.post('method_2')
    ])
  },
  types: ['ACTION', 'ACTION_SUCCESS', 'ACTION_FAIL']
}

這個是 Erik Rasmussen 提出的 Ducks 解決方案,個人覺得這個方法完全符合語義,也正確地發揮了 Middleware 應有的作用。

####3. Reducer 應該分開定義

小項目可以寫在一個文件中,但是有一定規模之後就不能這麼做了。你需要盡量將你的數據解耦,按照依賴鏈條劃分 Reducer Tree 在不同的文件中進行定義,然後用 combineReducers 進行組合。

7 Likes
#5

多谢指教,对于得出第一个缺点,可能是因为我一直在用flux,直接从store中获取数据习惯了这种获取数据的方式。

顺便问下,如果对于2套业务逻辑完全不同的页面,是否用2套独立redux的流程会更好呢?

#6

看你的具體需求咯,如果僅僅是業務邏輯不同,但是很多頁面元素都可以復用,仍然是屬於同一個項目,那麼為何不繼續使用同一個 store 呢?只需要在 store 的根目錄下做出兩條分支就能處理這種情況了,而且並不會有效率的負擔。

#7

也是最近稍微研究了下Redux,感觉远比Flux复杂,这和React的简单直观特性有点格格不入。尤其是异步处理那一块,本来一个Promise就能直观的解决的问题,Redux则先把其复杂化,分为各种action,然后再用一个复杂的middleware方案简化问题,最终回到Promise。也许是还没深入下去,实在无法理解其解决了什么本质问题。在我看来想在一个state对象里存储整个application的所有状态,这个本身就是一个太过激进的方案。

#8

不只是你,很多人都覺得 Redux 比 Flux 複雜。其實這隻是思想的一個轉換。如果妳學過函數式,你就會感覺到 Reactive Programming 就是應該這樣寫的。

Redux 能讓你的程序只擁有一個 Listener,你不需要在每一個組件中進行一套自己的 state 管理,一切所需的數據都從上游作為 props 傳進來了。

就異步 API 這一塊來說,Flux 的實現其實更加複雜,你需要在每一個 Action Creator 裡進行中間過程的事件管理,而在 Redux 中使用 Middleware ,妳什麼都不用管,直接構造一個描述性的 Action 就可以了。

還有,使用一個 store 並不是激進的做法,反而是簡化了程序的結構。在 Flux 中妳可以給每一個 store 定義一套相互獨立的 API ,所以在大項目中很快就會產生混亂,你的團隊也需要對每一個 store 的接口進行理解才能開始寫。而 Redux 中妳只要設計一個清晰的樹狀數據結構,所有人只要看一眼數據的形狀就能開始寫了。

5 Likes
#9

个人感觉: flux 相比 redux, 类似“分封制” 相比 “郡县制”。

redux不管什么都要从中央走一遍。村里老王家多养只鸡:chicken:, 要先汇报中央,
再下发通知给 省 - 市 - 县 - 乡 - 村,所有的一举一动,都被记录在案,随时可追溯。
看起来无比清晰,但是感觉总是有问题的。

5 Likes
#10

:smile:

#11

刚看了几天的redux,感觉redux技能get,又拉出了rxjs这个概念
推开一扇门之后看到了更多的门…

1 Like
#12

哥们是看过百家讲坛吧,哈哈,封分制,郡县制。

#13

就类似于java中的拦截器

#14

有图这是太好了,!!!

#15

回复有理有据,高技能的装逼

#17

这是redux必须的吗? 有没有不用Provider的redux的例子?

#18

这个问题看来没办法根本解决啊,使用immutable配合shouldComponentUpdate,可以使得数据的变动不导致到同级别的组件render,但是父组件肯定要render,假如层级比较深的话,比如会有性能问题了。

1 Like
#19

根本就不需要手动控制shouldComponentUpdate, connect 已经做了优化,将组件用到的数据,仅用到的数据用 connect 包下会过滤掉不必要的 render。connect 类似白名单这种东西,仅对比用到的 props 数据和相应的 store 数据,有变化则 render

1 Like
#20

赞同 @suhaotian 所说的,用 connect 生成稍微细粒度一些的 container 就可以解决一部分性能问题。比如针对 list 的情况,大部分时候是对 list 做 container ,但 item 交互比较多,数据经常变的情况下也可以对每个 item 做 container ,充分利用 connect 自带的性能优化。connect 生成的组件自己实现了 shouldComponentUpdate 方法,并且内部是进行 shallow equal 比较的。

#21

看着看着就看不懂了。。。。

#22

有没有更简单点的 学习edux 卡在这里了

1 Like