理解Redux数据流
对于刚刚接触redux的前端开发者来说,要想一下子直接理解redux内部的数据流向可能有点不太现实。但经过一段时间的总结和上手一个简单的redux项目,相信我们对redux内部的数据流向及内部原理就能够掌握得比较清楚了。
Redux简介
现在我们就从Redux是什么,Redux的三大原则和Redux的核心API开始介绍Redux,并说明Redux如何与React结合使用,以及它在Flux基础上的改变。
Redux是什么
我们都知道Flux它本身既不是库,也不是框架,而是一种应用的架构思想。而Redux呢,它的核心代码可以理解成一个库,但同时也强调和Flux类似的架构思想。
从设计上看,Redux参考了Flux的设计,但是对Flux许多冗余的部分做了简化,同时将函数式编程的思想融合其中。
非常有意思的是,Redux是从一个实验开始的,作者并没有想到Redux会变得如此重要又被广泛使用,他只是为了通过Flux思想解决他的热重载及时间旅行的问题而已。
Redux本身非常简单,它的设计思想与React有异曲同工之妙,均是希望用最少的API实现最核心的功能。Redux本身只把自己定位成一个可预测的状态容器。
“Redux”本身指redux这个npm包,它提供若干API让我们使用reducer创建store,并能够更新store中的数据或获取store中最新的状态。而“Redux应用”则是指使用了redux这个npm包并结合了视图层实现(如React)及其他前端应用必备组件(路由库,请求库等)组成的完整的类Flux思想的前端应用。
Redux三大原则
想要理解Redux,必须要知道Redux设计和使用的三大原则。
单一数据源
在传统的MVC框架中,我们可以根据需要创建无数个Model,而Model之间可以互相监听、触发事件甚至循环或嵌套触发事件,这些在Redux中都是不允许的。
因为在Redux的思想里,一个应用永远只有唯一的数据源。我们的第一反应可能是:如果一个复杂应用,强制要求唯一的数据源岂不是会产生一个特别庞大的JavaScript对象。
实际上,使用单一的数据源的好处在于整个应用状态保存在一个对象中,这样我们随时可以提取出整个应用的状态进行持久化。此外,这样的设计也为服务端渲染提供了可能。
至于我们担心的数据源对象过于庞大的问题,我们可以通过combineReducers化解。
状态是只读的
在Redux中,我们并不会自己用代码来定义一个store。取而代之的是,我们定义一个reducer,它的功能是根据当前触发的action对当前应用的状态进行迭代,这里我们并没有直接修改应用的状态,而是返回了一份全新的状态。
Redux提供的createStore方法会根据reducer生成store。最后,我们可以利用store.dispatch方法来达到修改状态的目的。
状态修改均由纯函数完成
在Redux里,我们通过定义reducer来确认状态的修改,而每一个reducer都是纯函数,这意味着它没有副作用,即接受一定的输入,必定会得到一定的输出。
这样设计的好处不仅在于reducer里对状态的修改变得简单、纯粹、可测试,更有意思的是,Redux利用每次新返回的状态生成酷炫的时间旅行调试方式,让跟踪每一次因为触发action而改变状态的结果成为了可能。
Redux核心API
Redux的核心是一个store,这个store由Redux提供的createStore方法生成。要想生成store,必须传入reducers。
在Redux里,负责响应action并修改数据的角色是reducer。reducer本质上是一个纯函数,它有两个参数state和action。reducer的职责就是根据state和action计算出新的state。
在实际应用中,reducer在处理state时,还需要有一个特殊的非空判断。很显然,reducer第一次执行的时候,并没有任何的state,而reducer的最终职责是返回新的state,因此需要在这种特殊情况下返回一个定义好的state。这也就是我们会定义一个defaultState的原因。
下面来介绍一下Redux中最核心的API--createStore:
import { createStore } from 'redux';const store = createStore(reducers);
通过createStore方法创建的store是一个对象,它本身包含4个方法。
- getState():获取store中当前的状态。
- dispatch(action):分发一个action,并返回这个action,这是唯一能改变store中数据的方式。
- subscribe(listener):注册一个监听者,它在store发生变化时被调用。
- replaceReducer(nextReducer):更新当前store里的reducer,一般只会在开发模式中调用该方法。
在实际开发中,我们最常用的是getState()和dispatch()这两个方法。至于subscribe()和replaceReducer()方法,一般会在Redux与某个系统做桥接的时候使用。
简单的项目实践
在当前的项目中,模块化开发已经成为主流。个人认为在模块化开发中,最重要的便是目录结构的设计。
在这里我们对目录进行了比较细致的划分:
- components:该目录下存放的是组件,比如开发网站类的项目实经常用到的Header公共组件。
- pages: 该目录下存放的是网页,系统中各个网页放在该目录下。比如首页和详情页等。
- store:该目录存放actions、reducers、constants(常量文件:存放dispatch要用到的常量)和index(创建store)。
个人认为在Redux项目中这样的目录设计是比较符合模块化设计思想的目录。(仁者见仁智者见智)
接下来我就介绍一下Redux具体的步骤和流程。(已请求简单列表数据为例)
第1步:reducer
store中的数据是由reducer决定的,所以第一步先完成reducer。
const defaultState = {homeList: []
}
export default function(state = defaultState, action) {
switch(action.type) {
case GET_HOME_LIST:
return {
homeList: action.homeList
};
default:
return defaultState
}
}
第2步: action
export const getHomeList = {type: GET_HOME_LIST,
// 模拟从后端接收到的假数据
homeList: [0,1,2,3]
}
第3步: constant
一个应用的常量应该是唯一的,把常量放在一个文件夹里同时按照模块划分,有利于防止变量名的冲突。
// homeexport const GET_HOME_LIST = 'GET_HOME_LIST';
第4步: index
该文件是用来创建store的。
import { createStore,combineReducers} from 'redux'
import HomeReducer from './reducers/home/index';
// 多个 reducer 合并成一个
const rootReducer = combineReducers({
home: HomeReducer
})
// 创建 store 只能接收到 一个 reducer
// 所以 创建之前 合并一下
export default createStore(rootReducer)
第5步: 使用store
class App extends ImmutableComponent {render() {
return (
// store成为全局变量,任何组件都可以访问了
<Provider store={store}>
<BrowserRouter>
<Header />
<Route path="/" component={Home} exact/>
<Route path="/detail" component={Detail} />
</BrowserRouter>
</Provider>
);
}
}
import React, { Component } from 'react';import { connect } from 'react-redux';
import { getHomeList } from '../../store/actions/home';
class Home extends Component {
componentDidMount() {
this.props.fetchHomeList();
}
state = {}
render() {
return ( <div>
home
length: { this.props.homeList.length }
</div> );
}
}
// 获取数据
// state:整个store,home页面,只要home模块,过滤一下
// 过滤完结果(return)都会由 connect 传给你组件的 props
const mapStateToProps = (state) => {
return {
homeList: state.home.homeList
}
}
// 用户操作UI引起页面变化
// 发起一个action
// dispacth行为connect传给组件
const mapDispatchToProps = (dispatch) => {
return {
fetchHomeList() {
dispatch(getHomeList)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
希望这个流程能对我们学习Redux有一定的帮助。
[注] 以上代码只是一个Redux的流程,并不是完整的代码。
以上是 理解Redux数据流 的全部内容, 来源链接: utcz.com/a/24636.html