跨组件通信之Redux_简介_设计思想_react-redux_划分_异步

1. redux简介

2013年 Facebook 提出了 Flux 架构的思想,引发了很多的实现。2015年,Redux 出现,将 Flux 与函数式编程结合一起,很短时间内就成为了最热门的前端架构。Redux 由 Flux 演变而来,但受 Elm 的启发,避开了 Flux 的复杂性。

Redux 可以运行于不同的环境(客户端、服务器、原生应用),并且易于测试。Redux 在 Vue、Node、React 中都可以使用。 它体小精悍(只有2kB,包括依赖)。

需要使用Redux的项目:

  • 用户的使用方式复杂,如:模块特别多,跨模块要做数据通信
  • 不同身份的用户有不同的使用方式(比如普通用户和管理员)
  • 多个用户之间可以协作
  • 与服务器大量交互,或者使用了WebSocket
  • View要从多个来源获取数据

从组件层面考虑,什么样子的需要 Redux:

  • 某个组件的状态,需要共享
  • 某个状态需要在任何地方都可以拿到
  • 一个组件需要改变全局状态
  • 一个组件需要改变另一个组件的状态

2. Redux 设计思想和原则

Redux的设计思想:

  1. 视图和状态是一一对应的,也就是当数据改变,视图就会自动改变
  2. 所有的状态,保存在一个对象里面(唯一数据源)。

注意:flux、redux 都不是必须和 react 搭配使用的,因为 flux 和 redux 是完整的架构,在学习 react 的时候,只是将 react 的组件作为 redux 中的视图层去使用了。

Redux 的使用的三大原则:

  • 唯一的数据源,整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
  • State 是只读的,唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
  • 使用纯函数来执行修改,为了描述 action 如何改变 state tree ,你需要编写 reducers。Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。刚开始你可以只有一个 reducer,随着应用变大,你可以把它拆成多个小的 reducers,分别独立地操作 state tree 的不同部分。

纯函数:
Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。Reducer不是只有Redux里才有,之前学的数组方法reduce, 它的第一个参数就是一个reducer。
纯函数是函数式编程的概念,必须遵守以下一些约束。

  • 不得改写参数
  • 不能调用系统 I/O 的API
  • 不能调用 Date.now() 或者 Math.random() 等不纯的方法,因为每次会得到不一样的结果

由于 Reducer 是纯函数,就可以保证同样的State,必定得到同样的 View。但也正因为这一点,Reducer 函数里面不能改变 State,必须返回一个全新的对象,

3. redux 简单使用

说了这么多理论,都觉得有些枯燥了,下面我们来分析一个使用 redux 的简单案例:在 node 环境下通过 redux 实现对数字的加减操作。

首先在终端中执行下面的命令,安装 redux:
yarn add redux -S

index.js文件:

//解构出redux中的createStore方法
const {createStore} = require('redux')

/**
 * reducer是形式为(state,action)=>state 的纯函数
 * 在reducer中对state设置默认值
 * reducer的作用是通过判断action传过来的值,对数据进行操作
 * 当state变化后,需要返回一个新的state,而不是修改传入的参数
**/
const reducer = (state = {count: 1}, action) => {
    switch (action.type) {
        case "increment":
            return {
                count: state.count + 1
            }
        case "decrement":
            return {
                count: state.count - 1
            }
        default:
            return state;
    }
}

//使用createStore创建一个redux store,参数是reducer
//store里的方法有 dispatch subscribe getState replaceReducer
const store = createStore(reducer)

//手动订阅更新
store.subscribe(() => console.log(store.getState().count))

//派发事件
//改变内部state唯一的方法就是dispatch一个action
store.dispatch({type: 'increment'})	//2
store.dispatch({type: 'increment'})	//3
store.dispatch({type: 'increment'})	//4

通过上面这个案例,我们来分析一下 redux 中几个要点

  • Action:是一个 JavaScript 普通对象,是把数据从组件传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。
  • Reducer:是一个纯函数,接收两个参数(state,action),它的作用是通过 action 传过来的值,对数据进行操作,返回一个新的state
  • Store:用来集中管理 state dispatch action,使用 createStore() 创建一个 store ,它的参数是Reducer。Store 提供以下几个方法:
    • getState() 获取 state;
    • dispatch(action) 派发事件,更新 state;
    • subscribe(listener) 订阅事件,要先派发事件,再订阅事件;
    • subscribe(listener) 返回的函数可以注销订阅事件;
    • replaceReducer() 数据替换,使用频率不高。

总结:

项目中所有的 state 都以一个对象树的形式储存在一个唯一的 store 中。 改变 state 的唯一办法是把要做的修改变成一个普通对象,也就是 action。通过dispatch(action) 派发事件,在 reducer 函数中根据 action 传过来的内容操作 state,最后返回一个新的 state。通过 store.getState() 方法读取 state 里的数据。

4. Redux 流程

跨组件通信之Redux

1、store 通过 reducers 创建了初始状态;
2、页面加载时,view 通过 store.getState() 获取到 state 并挂载到自己的状态上;
3、当用户操作页面发生改变时,调用了 actions 的方法,创建了带有标识信息的 action ;
4、actions 将 action 通过调用 store.dispatch 方法发送到了reducer 中;
5、reducer 接收到 action 并根据标识信息对数据进行处理后,返回新的 state;
6、store 的 state 被 reducer 更改为新 state 的时候,store.subscribe 方法里的回调函数会执行,此时就可以通知 view 去重新获取 state;

5. 在 react 中使用 redux

Redux 和 React 之间没有关系。Redux 支持 React、Angular、Ember、jQuery 甚至纯 JavaScript。尽管如此,Redux 还是和 React 和 Deku 这类库搭配起来用最好,因为这类库允许你以 state 函数的形式来描述界面,Redux 通过 action 的形式来发起 state 变化。

react-redux 提供两个核心的api:

  • Provider: 提供store,根据单一store原则 ,一般只会出现在整个应用程序的最顶层。
  • connect: 用于连接展示组件和 Redux store。

点击查看在react项目中如何使用 react-redux 实现一个简单的计数器功能

6. Redux 划分

Redux 不支持多个 store。相反,只有一个单一的store和一个根级的reduce函数(reducer)。随着应用不断变大,你应该把根级的reducer拆成多个小的 reducers,分别独立地操作state树的不同部分,而不是添加新的stores。这就像一个 React 应用只有一个根级的组件,这个根组件又由很多小组件构成。

不同的业务模块有自己的reducers,使用 combineReducers 方法将所有的reducer整合到一起。

注意:

  1. 分离 reducer 的时候,每一个 reducer 维护的状态都应该不同;
  2. 通过 store.getState 获取到的数据也是会按照 reducers 去划分的;
  3. 划分多个 reducer 的时候,默认状态只能创建在 reducer 中,因为划分 reducer 的目的,就是为了让每一个 reducer 都去独立管理一部分状态;

用法:

import {combineReducers} from 'redux'

//IndexReducer可以作为一个模块导入进来,这里为了展示方便,都写在一个文件里
const IndexReducer=(state={isShow:true},action)=>{
    switch(action.type){
        case "change_map_val":
            return{
                isShow:!state.isShow
            }
        default:
            return state;
    }
}

const reducer=combineReducers({
    IndexReducer,	//注意:在connect中读取值的时候,要使用state.IndexReducer
    ...
})
export default reducer

7. Redux 异步

通常情况下,action 只是一个普通的对象,不能包含异步操作,这导致了很多创建 action 的逻辑只能写在组件中,代码量较多不便于复用,组件的业务逻辑也不清晰,使用异步中间件后,可以通过actionCreator异步编写action,这样代码就会拆分到 actionCreator中,可维护性大大提高,可以方便于测试、复用,同时 actionCreator 还集成了异步操作中不同的 action 派发机制,减少编码过程中的代码量。

点击查看 Redux 异步库之 Redux-thunk 和 Redux-saga 的使用方法及区别

猜你喜欢

转载自blog.csdn.net/Charissa2017/article/details/105831041