引言 React的核心是使用组件定义界面的表现,是一个View层的前端库,简单来说它就是一堆前端组件,view,view,view,重要的事情说三遍。 可是有了组件以后,要怎么组织前端界面的表现呢?怎么和数据交互呢?怎么管理组件与组件之间的关系呢? 嘿嘿,你猜? Redux?是的, Redux可以做这些事情,而且做得很好!那么Redux是什么呢?怎么像踢球一样使用Redux搭建前端React应用程序?
1.Redux 应用管理服务
在了解是什么是Redux之前,可能需要先知道什么是Flux。Flux是Facebook用来构建用户端的web应用的应用程序体系架构。它通过利用数据的单向流动为React的可复用的视图组件提供了补充。
Flux应用包含三个部分:dispatcher, store, view.
单向的数据流是Flux应用的核心特性, Dispatcher,Store和View是拥有清晰的输入输出的独立节点。而Action是包含了新的数据和身份属性的简单对象。
用户的交互可能会使view产生新的action,这个action可以在整个系统中传播:
也就是说用户操作view时产生action,action通过dispatcher分发到不同的store,store里面保存状态(state)的信息,然后view监听到store中状态的变化后,进行view的重新渲染。
可是Facebook官方的Flux实现很多人都觉得很冗余,很难让人有使用它的欲望,当然GitHub上也涌现了一批关于Flux的框架,比如:Redux, Reflux, flummox,Alt 等。其中最火,逼格最高的就是Redux。
Redux是应用状态管理服务, 它本身受到了Flux的深远影响, 但是其核心概念确很简单:
1)actions, 用于描述View发生的事件及相关信息,且进行与数据相关的操作,并将数据传输到reducer。
2) reducers(多个reducer), reducer是一个函数:
(previousState, action) => newState
用来执行根据指定 action 来更新 state 的逻辑。reducer不存储state,也不直接改变state对象,而是返回新的state对象。
3) 只有一个store(树形结构),state 以单一对象存储在 store 对象中, 它是只读的,只能使用函数reducer对其进行更新(其实是返回新的state对象)。
2. Redux对比Reflux
在众多的关于Flux思想的类库中,Reflux 也是一个比较好的框架,它使用起来甚至比Redux更简单。它的单向数据流模式主要由actions和stores组成。
Reflux的特点:
1) 摒弃了dispatcher
2) stores可以监听actions的行为,并负责与数据相关的内容,比如从服务器获取数据并显示的触发stores的更新
3) stores也可以互相监听,可以做一些数据聚合的操作
4) sores提供了方法供view进行监听,以更新view
从上图以及其特点看,Reflux是以store为中心的,它具有多个store,store会监听action,以及提供方法供view监听。具体的使用起来也是非常的简单。
Redux对比Reflux:
Redux | Reflux | |
---|---|---|
store | 只有一个,以state形式存在 | 有多个store |
数据相关操作 | 在actions中进行 | 在store中进行 |
返回数据 | 返回一个新的state | 返回修改的store |
组件获取数据 | 将state合并到组件的props中 | 直接修改组件的state |
为什么会使用Redux,而不选择Reflux呢?其实Reflux更加的适合一些中小应用,由于其store都是独立的,而且store可以相互监听,当组件比较多时,组织起来就会很混乱,而且不同的store同步也会比较麻烦。在这一点上Redux做得就比较好了,它只有一个store,记录了应用的所有状态。当然Redux对于新手来说较Reflux会难懂一些,但是其数据组织却更加清晰。而且Redux还有一个调试神器,Redux-DevTools,通过它我们可以很方便的查看应用的整个状态树,以及状态的变化过程。
3. Redux 管理前端React组件
Redux 和 React 其实是没有必然关系的,Redux 用于管理 state,与具体的 View 框架无关。Redux 可以搭配 React、Angular 甚至纯 JS。不过,Redux 特别适合那些 state => UI 的框架,比如:React,因为 React 允许以 state 的形式来描述界面,而 Redux 非常擅长控制 state 的变化。
那么Redux和 React要怎么联系起来呢?我们需要Redux的React绑定库react-redux。
react-redux提供两个关键模块:Provider和connect。
1) Provider模块是整个App的容器,需要把原有的App 根组件包括起来并接受Redux的store作为props来注入Redux store。
import ReactDOM from 'react-dom'; import { Component } from 'react'; import { Provider } from 'react-redux'; import rootReducer from '../reducers'; import thunk from 'redux-thunk'; import { createStore, applyMiddleware } from 'redux';
class App extends Component { render() { // ... } } var store = createStore(rootReducer, applyMiddleware(thunk)); const targetEl = document.getElementById('root');
ReactDOM.render( <Provider store={store}> <App /> </Provider>, targetEl );
其中的createStore方法可以传入不同的第三方件,比如实现异步操作以及记录日志等。
2) connect模块将包装好的React组件连接到Redux 。连接操作不会改变原来的组件类,而是返回一个新的已与 Redux store 连接的组件类。
import { Component } from 'react';
export default class Counter extends Component { render() { return ( <button onClick={this.props.onIncrement}> {this.props.value} </button> ); } }
import { Component } from 'react'; import { connect } from 'react-redux'; import Counter from '../components/Counter'; import { increment } from '../actions'; import { bindActionsCreators } from 'redux';
// 哪些 Redux 全局的 state 是我们组件想要通过 props 获取的? function mapStateToProps(state) { return { value: state.counter }; }
// 哪些 action 创建函数是我们想要通过 props 获取的? function mapDispatchToProps(dispatch) { return { onIncrement: bindActionCreators(increment, dispatch) }; }
export default connect( mapStateToProps, mapDispatchToProps )(Counter);
其中mapStateToProps是一个函数,返回值表示的是需要合并到展示组件props中的state。 mapDispatchToProps也是一个函数,返回值表示的是需要合并到展示组件props中的actionCreators。
Redux 的 React 绑定库包含了容器组件和展示组件相分离的开发思想。
明智的做法是只在最顶层组件(如路由操作)里使用 Redux。其余内部组件仅仅是展示性的,所有数据都通过 props 传入。
容器组件 | 展示组件 | |
---|---|---|
位置 | 最顶层,比如路由处理 | 中间和子组件 |
是否绑定Redux | 是 | 否 |
读取数据 | 从Redux获取state | 从props获取数据 |
修改数据 | 向Redux派发actions | 从props调用回调函数 |
关于Redux和React的关系我们再举一个生动的例子:
我们以足球举个例子,欧洲杯今天凌晨刚刚落幕,在球赛中,主要由球场,球员以及足球组成,足球运动员跟随着足球在球场上的位置是不断变化的。那么足球就相当于 Redux的state;球场和球员则相当于React组件,由于球员只能在球场跑动,所以球员就相当于展示组件,而球场则是和Redux绑定的容器组件;球员踢球的动作相当于Redux的actions。整个过程就是球员(展示组件)踢球(actions),足球的位置(state)发生变化,球员跟随着足球(state)进行相应的跑动,进而形成单向的循环。
4.Redux在普元数字化企业云平台中的应用
我们先来看一下普元数字化企业云平台的整体逻辑图(红框标记的是前端所处的位置):
再来看一下主前端数据流动图:
React作为前端的界面(view),主要以组件(component)的形式存在,它会绑定Redux的actions以及state,用户操作时产生action,action此时会根据需要去Portal Server获取相关的数据,之后将数据传输到对应的reducer中,reducer作为一个函数,将得到的数据放到state中,并返回一个新的state,之后state中的部分属性会合并到组件的props中,进而进行view的更新。
5. 附录
Redux 中文文档:http://cn.redux.js.org/ Redux-DevTools: https://github.com/gaearon/redux-devtools
关于作者:
郭怀成
现任普元SOA产品部高级软件开发工程师,为普元新一代数字化企业云平台的一员,目前专职前端开发,对前端技术有着浓厚的兴趣。