理解|理解 redux

【理解|理解 redux】redux是当下流行的web前端所使用的一个状态管理架构。它本身是一个独立的构造,没有与任何其它库的耦合。由于reactjs的流行, 它自然而然的成为了react状态管理的延伸架构。 本文不打算讨论redux的优点以及细节, 只是想从纯粹的设计角度去理解redux状态管理的模式。
概览 redux把状态管理抽象成3个要素, action, reducer, store。
action是状态变化的描述性数据结构, 即一个标签(type)来标识action的名字, 和相关的数据

{ type: "添加订单", text: "Eat meal" }

reducer是action的handler, 是一个函数来具体执行状态变化操作.
store是状态对象和reducer的管理器, 在使用redux的时候, 你不应该直接执行reducer, 这破坏了它的设计意图。你要把它放到store中, 通过store去执行(dispatch)状态变化操作, 你也要通过store来获取状态对象。
更具体的描述:
  1. action, 描述发生了什么的对象, action是store的唯一信息来源, 通过store.dispatch(action)产生,
    而dispatch就是去调用reducer更新状态, 在store内部, currentState维持应用程序的当前状态:
    currentState = currentReducer(currentState, action)

    一个action的结构:
    { type: "ADD_TODO", text: "Eat meal" }

  2. reducer, 完成action具体的操作: (oldState, action)=>newState, 也就是handling action.
    写reducer时要求有初始化逻辑, 也就是如果state是undefined要给state赋值. 另外如果action未知时必须返回函数的state参数, 也就是旧的state.
    function todos(state, action) { if (state === undefined) state = []; switch (action.type) { case .... default: return state; } }

  3. store负责把action和reducer结合起来, store 有以下特性:
    • 获得状态 getState()
    • 更新状态 dispatch(action)
    • 登记监听器 subscribe(listener)
    store的建立需要通过redux的函数createStore,把reducer传入store, createStore函数的签名是这样:
    createStore(reducer, initialState)

    注意initialState永远不用赋值, 所以总是undefined, 这正好被用在reducer函数参数中来做初始化
有了以上设施, 整个流程就是先定义action, 然后是定义action handler也就是reducer, 然后把reducer传给createStore建立store:
store = createStore(reducer)

在运行createStore时会初始化state, 也就是它内部做一次dispatch,
... // 默认action export var ActionTypes = { INIT: '@@redux/INIT' } currentState = initialState // 默认是undefined,传给reducer做初始化 currentReducer = reducer ... dispatch({ type: ActionTypes.INIT }) ...

如果看dispatch函数, 他基本上就是去调用reducer,
currentState = currentReducer(currentState, action)

在初始化的dispatch时, action的值为 '@@redux/INIT', currentState为undefined
根据2的描述, 调用store=createStore(reducer)后, reducer返回的初始化状态对象被保存在store的currentState中, 通过store.getState()获取.
createStore建立后, store就成为调用reducer的代理, 通过store.getState()得到currentState, 通过store.dispatch(ACTION)再次调用reducer更新currentState.
总结 redux架构基于以下的约定构造:
  1. 函数reducer操作状态, 它的返回值构成了状态对象以及该对象的变化。但reducer不应该被直接调用, 状态对象被另一个独立的对象store(状态管理器)来维护. 在建立管理器时把reducer作为参数传递, 使得管理器可以调用它. 管理器通过暴露dispatch方法来调用reducer自身以改变状态, 同时状态对象(reducer的返回值)被封装在管理器中, 通过管理器暴露的方法getState获取.
  2. 状态信息被独立描述在action中, reducer的状态变化处理逻辑需要使用它. 它的结构包括变化类型(action.type), 以及变化的数据. reducer作为变化的具体处理者依据action的信息操作状态并返回新的状态
  3. reducer和action都由架构使用者定义和编码, redux提供的是管理器: 通过store=createStore(reducer)初始化状态对象, 通过store的其它方法来改变和获取状态. 而架构使用者只调用store方法来请求对状态的处理.
  4. 架构使用者在编码reducer时, 除了前述的约定以外, 还必须保证reducer是没有副作用的:
    a. 不允许直接改变state的值,只允许生成新的值
    b. 不应该有对外的API调用或者路由变化, 这些都应该在调用action前操作.
  5. 根(root) reducer,这个概念就是合并多个reducer, 形成一个单一的数据结构, 如:
    reducers:
    function visibilityFilter(state = VisibilityFilters.SHOW_ALL, action) { switch (action.type) { case ... default: return state } }function todos(state = [], action) { switch (action.type) { case ... default: return state } }

    root reducer:
    / * 合并reducer其实就是合并数据结构,将reducer函数的返回值包装在一个结构里. 根reducer通过将其它reducer的调用返回值包装在一起, 形成一个完整的状态数据对象结构. */export default function todoApp(state = {}, action) { // 在store.dispatch时, 下面的reducer函数将依次被调用, // 当一个传递给reducer的action参数不在switch里时, default会直接返回state参数, 见前述reducer代码 return { vsfilter: visibilityFilter(state.vsfilter, action), tdlist: todos(state.tdlist, action) } }

    最后, 应该传递给createStore的是这个根reducer:
    store = createStore(todoApp)

redux的核心代码(createStore)不过160行, 了解了核心部分其实就掌握了它的脉络。再去读官方文档就感觉清晰得多了。

    推荐阅读