迷你 Redux 实现

redux 主要由三部分组成:action, reduer, store,

Action

Action 是用户自己定义的,用来描述发生了什么, action 的 type 字段是必须的,其它字段可以自己定义:

const action = {
  type: 'ADD_TODO',
};

Reducer

Reducer 也是由用户负责编写的,Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。

function todoApp(state = initialState, action) {
  switch (action.type) {
    case ADD_TODO:
      return state + 1
    default:
      return state
  }
}

Store

redux 只有一个单一的 Store, 用户通过 createStore() 来创建 store,createStore() 接受一个 reducer 函数作为参数, 我们使用 combineReducers() 将多个 reducer 合并成为一个。

createStore() 的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。

let store = createStore(todoApp, window.STATE_FROM_SERVER)

Store 有以下职责:

实现 mini-redux

下面我们来实现 redux 核心的 createStore() 函数

export function createStore(reducer) {
  let currentState = {}
  let currentListeners = []

  function getState() {
    return currentState
  }

  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }
    currentListeners.push(listener)
  }

  function dispatch(action) {
    currentState = reducer(currentState, action)
    currentListeners.forEach(v => v())

    return action
  }

  // type 定义为一个特殊的值,触发 reducer 的 default 分支来获取初始 state
  dispatch({type: '@@mini-redux/INIT'})

  return { getState, subscribe, dispatch}
}

bindActionCreators

然后我们实现 bindActionCreators(),这个函数首先负责把 action 创建函数用 dispatch 包一层,然后返回有相同keys的一个对象,通常用于 react-redux 里的 connect 的第二个参数的映射。

// bindActionCreators

function bindActionCreator(creator, dispatch) {
  return (...args) => dispatch(creator(...args))
}

export function bindActionCreators(creators, dispatch) {
  let bound = {}
  Object.keys(creators).forEach(v => {
    let creator = creators[v]
    bound[v] = bindActionCreator(creator, dispatch)
  })

  return bound
}

加入中间件机制

下面我们加入中间件机制,完整代码如下:

export function createStore(reducer, enhancer) {
  if (enhancer) {
    return enhancer(createStore)(reducer)
  }
  let currentState = {}
  let currentListeners = []

  function getState() {
    return currentState
  }

  function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }
    currentListeners.push(listener)
  }

  function dispatch(action) {
    currentState = reducer(currentState, action)
    currentListeners.forEach(v => v())

    return action
  }

  // type 定义为一个特殊的值,触发 reducer 的 default 分支来获取初始 state
  dispatch({type: '@@mini-redux/INIT'})

  return { getState, subscribe, dispatch}
}

export function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    let dispatch = store.dispatch
    
    const midApi = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }

    let middlewareChain = middlewares.map(middleware=>middleware(midApi))
    dispatch = compose(...middlewareChain)(store.dispatch)

    // dispatch = middleware(midApi)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

export function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  } else if (funcs.length ===1) {
    return funcs[0]
  }

  return funcs.reduce((ret, item) => (...args) => ret(item(...args)))
}

// bindActionCreators

function bindActionCreator(creator, dispatch) {
  return (...args) => dispatch(creator(...args))
}

export function bindActionCreators(creators, dispatch) {
  let bound = {}
  Object.keys(creators).forEach(v => {
    let creator = creators[v]
    bound[v] = bindActionCreator(creator, dispatch)
  })

  return bound
}

上面就把一个最简单的 Redux 给实现了。