Redux原理

512 阅读1分钟

前言

Redux是一个管理状态的容器,是一种状态管理方案,它可以配合任何一种前端框架使用。大多数情况下,我们都是在React基础上使用Redux的。React-Redux库提供了Provider和connect等组件,使得我们能够比较优雅地在React中使用Redux。

本文只针对Redux,对于React-Redux或许会另开一篇做分析。

正文

注:以下出现的Redux源码都是经过简化的

Store

Redux做的事情非常简单,就是提供一个地方来存储状态,当状态被改变时发送通知。这个储存状态的地方,在概念上称为store。我们可以通过createStore方法来实现一个store。

首先来看下Redux中的createStore方法。

function createStore(reducer, initialState) {
  // 初始状态
  let state = initialState
  // 订阅的函数
  let listeners = []

  // 订阅
  function subscribe(listener) {
    listeners.push(listener)

    // 取消订阅
    return function unsubscribe() {
        const index = listeners.indexOf(listener)
        listeners.splice(index, 1)
    }
  }

  // 改变状态
  function dispatch(action) {
    // 更新state
    state = reducer(state, action)
    // 触发订阅函数
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
  }

  // 获取state
  function getState() {
    return state
  }

  return {
    subscribe,
    dispatch,
    getState
  }
}

我们可以看到,store不仅保存了状态数据,还提供了改变状态和监听动作的方法。

Redux使用发布-订阅模式来实现状态的改变后通知。在这里,state用于保存状态;subscribe方法将需要订阅的函数保存到listeners中;dispatch方法用于改变状态,并且调用listeners中已订阅的函数。

实际使用过程中,我们首先会调用createStore方法来创建一个store,createStore接收两个参数reducer和initialState。initialState顾名思义就是初始的状态,比如0、'Jack',也可以是一个对象,比如{ count: 1 }。那么reducer是啥呢?

Reducer & Action

createStore中的dispatch方法。

function dispatch(action) {
  state = reducer(state, action)
  // ...
}

我们可以看到,当我们调用dispatch去改变状态时,并不是随意地赋值给state,而是经过的reducer的一层处理。

Reducer使状态的改变变得可控,最终的状态变得可预期。打个比方,将Reducer看作一个工厂,它提供几条生产线,我们只需将原料和需要走哪条生产线告诉Reducer就行了。而原料和预定的线路就包含在Action中。

下面是一个简单的例子。

function reducer(state, action) {
  switch (action.type) {
    case 'INCREASE':
      return state + 1
    case 'DECREASE':
      return state - 1
    default:
      return state
  }
}

const action = { type: 'INCREASE' }

const store = createStore(0, reducer)

store.dispatch(action)

console.log(store.getState()) // 1

可以看到,有了reducer和action,我们就不会随意地将状态改为任意值,而是从Reducer这个工厂中选择一条生产线,将状态进行加工,得到最终的状态。所谓可预期。

综上所述,包括store、reducer以及action,便是Redux的核心内容。

combineReducers

在实际项目中,状态肯定不止一个,几十上百个的都很常见。如果这么多的状态只用一个reducer方法来处理,那我们肯定是无法接受的。Redux提供了一个combineReducers方法,用来整合多个reducer方法。这样,我们就可以将状态拆分开来进行管理。以下是简化后的combineReducers源码。

function combineReducers(reducers = {}) {
  // 状态名的数组集合
  const keys = Object.keys(reducers)

  // 返回经整合的reducer方法
  return function combination(state, action) {
    const nextState = {}

    // 遍历每个状态并作更新
    keys.forEach(key => {
      const reducer = reducers[key]
      const previousStateForKey = state[key]  // 旧状态
      const nextStateForKey = reducer(previousStateForKey, action)  // 调用对应的reducer得到新的状态

      nextState[key] = nextStateForKey
    })

    return nextState
  }
}

举一个简单的例子。

function count(state, action) {
  switch (action.type) {
    case 'INCREASE':
      return state + 1
    case 'DECREASE':
      return state - 1
    default:
      return state
  }
}

function name(state, action) {
  switch (action.type) {
    case 'SET_NAME':
      return action.name
    default:
      return state
  }
}

const reducer = combineReducers({ count, name })

总结

Redux原理就是发布订阅,本身很简单,源代码量也很少,学习起来比较轻松,主要是体会其思想。