Flux是Facebook用来构建用户端的Web应用程序的体系架构,与其它形式化的框架相比,它更像是一个架构思想,用于管理和控制应用中数据的流向。这里应用中的数据指包括但不限于来自服务端的数据页面中view的一些状态(如一个面板是展开仍是关闭),临时存储在本地须要持久化到服务端的数据等。javascript
好了,说了这么多好像仍是一脸懵逼,不慌,接下来看看展开式。css
在讲述Flux以前,咱们看看以前传统的MVC架构以及在前端中的一些问题继而思考Flux带来的改变。MVC(Model-View-Controller)最早兴起于后端,经过对应用程序复杂度的简化使程序更加直观和便于维护。后端程序MVC中View能够看为数据的呈现,Model为数据的模型,Controller做为程序的流程控制。如今假设有这样的场景,用户想查看本身的profile页面,可能会有这样的流程:在页面上点击profile按钮,接下来就是一个HTTP请求(/profile?username=jiavan) => Controller接收到这一请求并得到请求的内容username=jiavan而后告知Model须要jiavan的数据 => Model返回了jiavan的数据 => Controller获得数据返回新的视图,看下流程:html
如今前端中又有这样的场景:切换Menu中的Item,当前选中的Item颜色不一样于其它颜色而且底部显示对应Item的内容。通常状况下咱们会定义一个css class来做为当前选中Item的样式。当用户点击Item_A为被点击的元素新增高亮的class,其它兄弟元素移除该样式,这里的事件响应函数就是Controller,咱们会在这里处理样式修改逻辑,以及更新Model的数据,而后新的数据及样式从新渲染界面。这种VC<->M
的形式在关系比较简单的状况下是比较清晰容易控制的,可是复杂的页面上这样的模式可能会变得很是混乱:前端
之因此变得混乱了,由于不少view都具有修改多个model的能力,这里的单个修改行为能够称之为一个Action,一个Action的产生多是用户行为,或者一个Ajax请求须要渲染新界面。对比上面后端传统MVC模式能够发现:java
那么是否是能够把前端中修改状态即state的行为(事件回调/Ajax)所有抽象成一种Action描述,而后交付到一处即Reducers来进行原子化处理,而后Reducer修改整个应用中惟一的一棵state tree即Store,最后经过state->view的机制来从新渲染?react
上面提到的几个概念已经对Flux有了初步的了解,下面进入正题。相信有了解Flux的都应该看过下面这张著名的数据流图:git
因此Flux能够被看做传统MVC的改进而非颠覆,当我第一次看到Flux的时候实际上是比较懵逼,但看到并使用了Redux后确实有一种很是惊艳的感受。github
按照Redux官方的描述Redux is a predictable state container for JavaScript apps.
,其中predictable
和state container
体现了它的做用。那么如何来理解可预测化
的呢?这里会有一些函数式编程方面的思想,在Redux中reducer函数是一个纯函数,相同输入必定会是一致的输出,因此肯定输入的state那么reducer函数输出的state必定是能够被预测的,由于它只会进行单纯的计算,保证正确的输出。状态容器
又是什么?说明Redux有一个专门管理state的地方,就是Store,而且通常状况下是惟一的,应用中全部state造成的一颗状态树就是Store。Redux由Flux演变而来,但受 Elm 的启发,避开了 Flux 的复杂性,咱们看看其数据流向:编程
不一样于Flux架构,Redux中没有dispatcher这个概念,而且Redux设想你永远不会变更你的数据,你应该在reducer中返回新的对象来做为应用的新状态。可是它们均可以用(state, action) => newState
来表述其核心思想,因此Redux能够被当作是Flux思想的一种实现,可是在细节上会有一些差别。redux
一个object tree
的形式存储在一个单一的store中;动做行为的抽象
;这里须要说明一点的是reducer函数,它应当是一个纯函数,不该该有反作用,不该有API调用,Date.now()
或者随机获取等不稳定的操做,应当保证相同的输入reducer计算的结果应该是一致的输出,它只会进行单纯的计算。编写reducer函数也是Redux中比较重要的一块,它的形式以下:
function testReducer(state, action) { switch (action.type) { case ACTION_TYPE: // calc... return newState; default: return state; } return newState; }
state是不可修改的,因此返回的新state应该是基于输入state副本的修改,而不是直接修改state后的返回。
1. 单一数据源,store
整个应用的state被存放在一棵Object tree中,而且这个Object tree只存在惟一一个store中;
2. state是只读的
惟一能改变state的方法是触发action,action是对已经发生了的事情的抽象描述,简单的讲,它把行为抽象成了一个对象。
好比,删除一条记录的action能够抽象的理解为:
{ type: 'DELETE_ITEM', index: 1, }
3. 使用纯函数来实现state归并操做,reducer
传入待修改的state和一个告知reducer如何修改state的action,reducer将返回action规则对应下操做后的新的state。
reducer(state, action) => new state
严格的单向数据流是Redux设计的核心
Redux应用数据的生命周期遵循下面4个步骤:
新的state树就是应用的下一个状态
,如今就能够根据新的state tree来渲染UI。
咱们经过一个很是简单的计数器demo来梳理Redux的数据流。
0x00. 建立action
action其实就是一个普通的对象,只是对行为的抽象描述,这里咱们能够把加上一个数描述为:
{ type: INCREMENT, //该动做的抽象描述 number, // 该动做携带的数据 }
更多的时候咱们会经过一个action生成函数来获得一个action:
function incrementCreator(number) { return { type: INCREMENT, number, }; }
0x01. 建立reducer函数
reducer做为整个Redux中action的处理中枢,接收state与action并对此修改数据,返回应用的下一个状态。
function countReducer(state, action) { switch (action.type) { case INCREMENT: return Object.assign({}, { counter: state.counter + action.number, }); case DECREMENT: return Object.assign({}, { counter: state.counter - action.number, }); default: return state; } }
注意:上面咱们已经提到屡次,state是不可修改的,因此经过assign
归并咱们对数据的操做,返回的是state副本修改后的对象,并不是直接修改了输入的state。
0x02. 建立惟一store
经过Redux中的createStore方法传入reducer函数来建立整个应用的store。
const store = createStore(countReducer);
0x03. 修改state
经过store的dispatch方法来发起一个action。
store.dispatch(incrementCreator(5)); store.dispatch(decrementCreator(4));
import { createStore } from 'redux'; // actions const INCREMENT = 'INCREMENT'; const DECREMENT = 'DECREMENT'; // actionCreator,能够视为建立action的语法糖 function incrementCreator(number) { return { type: INCREMENT, number, }; } function decrementCreator(number) { return { type: DECREMENT, number, }; } // 初始化state const initialState = { counter: 0, }; // reducers函数,注意最后必定要return state防止不能匹配到action的时候state丢失 function countReducer(state = initialState, action) { switch (action.type) { case INCREMENT: return Object.assign({}, { counter: state.counter + action.number, }); case DECREMENT: return Object.assign({}, { counter: state.counter - action.number, }); default: return state; } } // 建立store const store = createStore(countReducer); // 订阅store的修改 const unsubscribe = store.subscribe(function log() { console.log(store.getState()); }); // 经过dispatch action来改变state store.dispatch(incrementCreator(5)); //Object {counter: 5} store.dispatch(decrementCreator(4)); //Object {counter: 1} // 取消订阅 unsubscribe();
原文出处 https://github.com/Jiavan/jia... 以为对你有帮助就给个star吧