« 回到主页

Redux 综述

1 Redux介绍

Redux是JavaScript状态容器,提供可预测化的状态管理。

1.1 Redux的要点

1.2 Redux的动机

随着JavaScript单页应用开发日趋复杂,JavaScript需要管理比任何时候都要多的state,管理不断变化的state非常困难,如果一个model的变化会引起另一个model变化,那么当view变化时,就可能引起对应model以及另一个model的变化,依次地,可能会引起另一个view的变化。 Redux试图让state的变化变得可预测。

1.3 Redux的三大原则

1.3.1 单一数据源

整个应用的state被储存在一棵对象树中,且这个对象树只存在于唯一的一个store中。

1.3.2 state是只读的

唯一改变state的方法是触发action,action是一个用于描述已发生事件的普通对象。

1.3.3 使用纯函数来执行修改

为了描述action如何改变state tree,需要编写reducers。

1.4 Redux的先前技术——Flux

1.4.1 Redux与Flux的联系

Redux的灵感来源于Flux的几个重要特性。和Flux一样,Redux 规定,将模型的更新逻辑全部集中于一个特定的层(Flux里的store,Redux里的reducer)。Flux 和 Redux 都不允许程序直接修改数据,而是用一个叫作 “action” 的普通对象来对更改进行描述。

1.4.2 Redux与Flux的不同

不同于Flux,Redux并没有dispatcher的概念,原因是它依赖纯函数来替代事件处理器,纯函数构建简单,也不需额外的实体来管理它们。Flux常常被表述为 (state, action) => state,从这个意义上说,Redux无疑是Flux架构的实现,且得益于纯函数而更为简单。 和Flux的另一个重要区别,是Redux设想永远不会变动数据,可以很好地使用普通对象和数组来管理state ,而不是在多个reducer里变动数据。正确且简便的方式是,在reducer中返回一个新对象来更新state。

2 Redux基础

2.1 Action

Action是把数据从应用传到store的有效载荷,它是store数据的唯一来源,一般来说会通过store.dispatch()将action传到store。 Action本质上是JavaScript普通对象。约定,action内必须使用一个字符串类型的 type字段来表示将要执行的动作。多数情况下type会被定义成字符串常量,当应用规模越来越大时,建议使用单独的模块或文件来存放action。除了type字段外,action对象的结构完全由自己决定。 应该尽量减少在action中传递的数据,如传递对象的index就比传递整个对象好。 Action创建函数就是生成action的方法,action和action创建函数这两个概念很容易混在一起,使用时最好注意区分。 在传统的Flux实现中,当调用action创建函数时,一般会触发一个dispatch:

function addTodoWithDispatch(text) {
    const action = {
        type: ADD_TODO,
        text
    }
    dispatch(action)
}

在Redux中,action创建函数只是简单的返回一个action,Redux只需把action创建函数的结果传给dispatch()方法,即可发起一次dispatch过程:

function addTodo(text) {
    return {
        type: ADD_TODO,
        text
    }
}
dispatch(addTodo(text))

或者创建一个被绑定的action创建函数来自动dispatch:

function addTodo(text) {
    return {
        type: ADD_TODO,
        text
    }
}
const boundAddTodo = (text) => dispatch(addTodo(text))
boundAddTodo(text);

store里能直接通过store.dispatch()调用dispatch()方法,但多数情况下会使用 react-redux提供的connect()帮助器来调用,bindActionCreators()可以自动把多个action创建函数绑定到dispatch()方法上。

2.2 Reducer

Action只是描述了有事情发生这一事实,并没有指明应用如何更新state。而这正是reducer要做的事情。 确定了 state 对象的结构,就可以开始开发 reducer。reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。 保持 reducer 纯净非常重要。永远不要在 reducer 里做这些操作:

2.3 Store

Store就是把Action和Reducer联系到一起的对象。Store有以下职责:

2.4 数据流

严格的单向数据流是Redux架构的设计核心,这意味着应用中所有的数据都遵循相同的生命周期。 Redux应用中数据的生命周期遵循下面 4 个步骤:

2.5 搭配React

Redux和React之间没有关系。Redux支持React、Angular、Ember、jQuery 甚至纯JavaScript,只是Redux和React这类框架搭配来用最好,因为这类框架允许以state函数的形式来描述界面,Redux通过action的形式来发起state变化。 Redux默认并不包含react-redux这一React绑定库,需要单独安装。

展示组件容器组件
作用描述如何展现(骨架、样式)描述如何运行(数据获取、状态更新)
直接使用Redux
数据来源props监听Redux state
数据修改从props调用回调函数向Redux派发actions
调用方式手动通常由React Redux生成

大部分的组件都应该是展示型的,但一般需要少数的几个容器组件把它们和store连接起来。 技术上讲,可以直接使用store.subscribe()来编写容器组件,容器组件就是使用store.subscribe()从state树中读取部分数据,并通过props来把这些数据提供给要渲染的组件。可以手工来开发容器组件,但不建议这么做,因为这样就无法使用React Redux带来的性能优化,建议使用React Redux的connect()方法来生成,这个方法做了性能优化来避免很多不必要的重复渲染。 使用connect()前,需要先定义mapStateToProps这个函数来指定如何把当前 store state映射到展示组件的props中。除了读取state,容器组件还能分发action。类似的方式,可以定义mapDispatchToProps()方法接收dispatch()方法并返回期望注入到展示组件的props中的回调方法。 所有容器组件都可以访问store,所以可以手动监听它。一种方式是把它以props的形式传入到所有容器组件中,但这太麻烦了,建议的方式是使用指定的React Redux组件来让所有容器组件都可以访问store,而不必显式地传递它,只需要在渲染根组件时使用即可。

« 回到主页