Flux与Redux

基于React的比较重要的思想和库,这里也简单的写个博客记录一下学习过程

Flux架构说到底就是一种思想,就比如说我们写一个组件的时候,就很容易在一个组件内又写请求数据的函数又把这些数据展示出来,就会让一个组件显得很长很长,但其实业务逻辑不需要关心数据从哪里来,只需要定义好传入的接口就行了,数据应该抽象到其他地方去做,比如把数据抽象到父组件中去。这样就完成了数据请求和业务逻辑的分开。我们称只有数据请求的组件叫容器型组件,而只有业务逻辑没有数据请求的组件叫展示型组件。这样抽象后的代码是具有更多的可读性和可维护性,但是!!!!这不是最佳的办法,因为请求数据这部分还是写在了组件当中,数据与逻辑解耦才是我们真正想要的!这是,Flux架构就来了。

Flux架构

Flux可以说是MVC模式的一种变体,更为清晰和简单,对于MVC可以自行搜索了解。Flux的核心思想就是数据和逻辑永久单向流动我们看一下他的流程图
flux
详细点就是
flux
简单的用语言说就是:

  1. 用户访问 View
  2. View 发出用户的 Action
  3. Dispatcher 收到 Action,要求 Store 进行相应的更新
  4. Store 更新后,发出一个”change”事件
  5. View 收到”change”事件后,更新页面

Redux

简单的了解了Flux后,我们就看看Redux,Redux就是基于Flux架构实现的一个react库,用于集中式管理状态,我们先看看他的原理图
redux

  • 这个原理图就是,比如我们在UI界面端点击了一个按钮,要把这个按钮从白色变成红色,在我们点击后会触发点击时间,调用了store.dispath()方法把一个action传给store,然后store就会再帮我们调用reducer函数。reducer是个纯函数,他会根据action修改相应的状态并返回给store,然后store更新状态,我们button的颜色就会相应更改了(store.getState())

我们用形象点的语言来解释一下

  • 我们是顾客,去一个饭店吃饭,ReactComponents就是菜单,是我们能看到的UI图,然后我们点菜,就会告诉店小二我们要吃啥,这个店小二就是ActionCreators,店小二就会拿着我们的想要的食物(action)去告诉(dispatch)老板,老板就是store,然后老板再把这个消息告诉后厨(Reducers),后厨知道我们要吃啥了,就刷刷刷加工给我们上食材(返回新状态)。

重点API

  1. store.getState() 获取状态
  2. store.dispatch() 更改状态
  3. store.subscribe() 监听状态的改变

注意中间件的使用!并且中间件一般是是在dispatch里生效的

react-redux

就是 Facebook团队基于redux的影响出了一个可以更好的使用redux的一个库。
重点有如下API

  • connect()
  • <Provider/>组件

先看看react-redux的模型图
react-redux
react-redux中把一个组件拆开,分成UI组件和容器组件(更贴合于Flux架构),然后UI组件不接受任何业务逻辑,就类似与纯函数,他就像一个“纯组件”,只通过接受过来的props参数来改变渲染结果,然后Count组件就负责承担业务逻辑和与redux沟通,并把store中的state以及更改state的方法(dispatch)分发给UI组件,而connect()方法就是起这个作用的。

connect()

const Container = connect(mapStateToProp,mapDispatchToProps)(UI)
这样Container就是一个被加工的容器组件,这传代码的意思是,connect方法接受两个参数:mapStateToProps和mapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。
从本质上说connect可以看作是一个高阶函数

mapStateToProps

  1. mapStateToProps函数返回的是一个对象;
  2. 返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
  3. mapStateToProps用于传递状态

mapDispatchToProps

  1. mapDispatchToProps函数返回的是一个对象;
  2. 返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
  3. mapDispatchToProps用于传递操作状态的方法

<Provider/>

Provider组件就是基于react中的Context实现的,connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。这里用Provider就可以直接让容器组件拿到了。具体源码实现这里就不放了。

代码格式

就看看redux在生产环境中是如何使用的吧,我们做出这样一个效果
ui
上方Count组件可以拿到下方Person组件的人数,然后Person组件也可以取到上方Count组件的求和数,先看项目文件
code

action

除了redux文件夹里面的东西要讲一讲外其他的都没啥必要了。那就按顺序,我们分析需求,要用到redux,就先把这些文件夹建起来,该装的依赖装好,然后分需求,看看有什么,Count组件要计算, Person组件要添加人数,然后根据这些需求去写action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// constant.js 文件
/*
该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const ADD_PERSON = 'add_person'

/*
该文件专门为Count组件生成action对象
*/
import {INCREMENT,DECREMENT} from '../constant'

//同步action,就是指action的值为Object类型的一般对象
export const increment = data => ({type:INCREMENT,data})
export const decrement = data => ({type:DECREMENT,data})

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const incrementAsync = (data,time) => {
return (dispatch)=>{
setTimeout(()=>{
dispatch(increment(data))
},time)
}
}

// Person组件生成action对象
import { ADD_PERSON } from "../constant"
export const addPerson = personObj => ({type:ADD_PERSON,data:personObj})

然后就根据action写reducer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* 
1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/

const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){
//从action对象中获取:type、data
const {type,data} = action
//根据type决定如何加工数据
switch (type) {
case 'increment': //如果是加
return preState + data
case 'decrement': //若果是减
return preState - data
default:
return preState
}
}

// Person
import {ADD_PERSON} from '../constant'
//初始化人的列表
const initState = [{id:'001',name:'tom',age:18}]

export default function personReducer(preState=initState,action){
const {type,data} = action
switch (type) {
case ADD_PERSON: //若是添加一个人
return [data,...preState]
default:
return preState
}
}

然后联合reducer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 
该文件用于汇总所有的reducer为一个总的reducer
*/
//引入combineReducers,用于汇总多个reducer
import {combineReducers} from 'redux'
//引入为Count组件服务的reducer
import count from './count'
//引入为Person组件服务的reducer
import persons from './person'

//汇总所有的reducer变为一个总的reducer
export default combineReducers({
count,
persons
})

然后创建store

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 
该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入汇总之后的reducer
import reducer from './reducers'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'

//暴露store
export default createStore(reducer,applyMiddleware(thunk))

至此,关于redux的文件就弄好了,接下来就去弄容器组件,Count组件要的state是Count和跟Person人数,Action是数字的运算,则有

1
2
3
4
5
6
7
 export default connect(
state => ({
count:state.count,
personCount:state.persons.length
}),
{increment,decrement,incrementAsync}
)(CountUI)

Person类似

1
2
3
4
5
6
7
8

export default connect(
state => ({
count:state.count,
personCount:state.persons.length
}),
{increment,decrement,incrementAsync}
)(CountUI)

至此,对于react的基本介绍就到这里了。