理解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

一个应用的常量应该是唯一的,把常量放在一个文件夹里同时按照模块划分,有利于防止变量名的冲突。

// home

export 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

回到顶部