Redux 源码简化

#1

周天下午心血来潮,摘抄了一下redux的源码(在项目中替换可用)

/**
 * @file Redux 源码注释
 */
export let ActionTypes = {
    INIT: '@@redux/INIT'
}
export function createStore (reducer, initialState) {
    let currentReducer = reducer
    let currentState = initialState
    let currentListeners = []
    let nextListeners = currentListeners
    let isDispatching = false

    /**
     * 避免 currentListeners 和 nextListeners 持有相同引用
     * 每次修改 nextListeners 前需要先从 currentListeners clone 一套出来
     */
    function ensureCanMutateNextListeners() {
        if (nextListeners === currentListeners) {
            nextListeners = currentListeners.slice()
        }
    }

    /**
     * 注册监听事件
     * @param  {function} listener
     * @return 返回删除 listener 事件的方法,方便运行环境持有和删除
     */
    function subscribe (listener) {
        ensureCanMutateNextListeners()
        nextListeners.push(listener)
        return () => unsubscribe(listener)
    }
    /**
     * 删除监听事件
     * @param  {function} listener
     */
    function unsubscribe (listener) {
        ensureCanMutateNextListeners()
        let index = nextListeners.indexOf(listener)
        ~index || nextListeners.splice(index, 1)
    }

    /**
     * 通过 reducer 执行action 修改当前状态 并广播事件
     * @param  {object} action
     */
    function dispatch (action) {
        currentState = currentReducer(currentState, action)
        currentListeners = nextListeners
        currentListeners.forEach(listener => listener())
        return action
    }

    function getState () {
        return currentState
    }

    /**
     * 动态加载 reducer 
     * 替换原有的 reducer 并初始化该reducer状态
     */
    function replaceReducer (nextReducer) {
        currentReducer = nextReducer
        dispatch({ type: ActionTypes.INIT })
    }

    dispatch({ type: ActionTypes.INIT })
    return {
        dispatch,
        subscribe,
        getState,
        replaceReducer
    }
}

/**
 * 展开 reducers 在顶级state中对应指定key的state.
 */
export function combineReducers (reducers) {
    let reducerKeys = Object.keys(reducers)
    return function combination(state = {}, action) {
        let nextState = {}
        return reducerKeys.filter(key => (
            state[key] !== (nextState[key] = reducers[key](state[key], action))
        )).length ? nextState : state
    }
}

/**
 * 批量创建 action 的快捷方式
 */
export function bindActionCreators(actionCreators, dispatch) {
    if (typeof actionCreators === 'function') {
        return bindActionCreator(actionCreators, dispatch)
      }
    let boundActionCreators = {}
    Object.keys(actionCreators).forEach(key => boundActionCreators[key] = bindActionCreator(actionCreators[key], dispatch))
    return boundActionCreators
}
function bindActionCreator (actionCreator, dispatch) {
    return (...args) => dispatch(actionCreator(...args))
}

/**
 * createStore 中间件
 * @param  {...[type]} middlewares [description]
 * @return {[type]}                [description]
 */
export function applyMiddleware (...middlewares) {
    return (createStore) => (reducer, initialState) => {
        let {
            dispatch,
            subscribe,
            getState,
            replaceReducer
        } = createStore(reducer, initialState)
        let chain = []
        let middlewareAPI = {
            getState,
            // dispatch 封装一层,方便使用集成过中间件的dispatch
            dispatch: (action) => dispatch(action)
        }
        chain = middlewares.map(middleware => middleware(middlewareAPI))
        dispatch = compose(...chain)(dispatch)

        return {
            dispatch,
            subscribe,
            getState,
            replaceReducer
        }
    }
}

/**
 * 嵌套执行方法
 * @param  {...function} funcs
 * @return {function} 组合结果方法
 *
 * @code
 * compose(a, b, c) 等价于 (...args) => a(b(c(...args)))
 */
export function compose (...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  } else {
    const last = funcs[funcs.length - 1]
    const rest = funcs.slice(0, -1)
    return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
  }
}

const Redux = {
    createStore,
    combineReducers,
    bindActionCreators,
    applyMiddleware,
    compose
}

export default Redux
2 Likes