redux 实现

redux适用于很多场景,需要用到全局存储状态的应用都可以用到它,不管是换肤的应用还是购物车的场景,需要将不同的组件通过相同的状态关联起来,或者相同状态的变化触发不同视图的更新,都很适合用到redux。

目标

实现 redux 接口:

  • createStore 创建store,接受reducer函数和初始化的状态state
  • combineReducer 合并多个reducer
  • bindActionCreator 转换action对象

分析

  • reducer 作用:存放和管理状态
  • dispatch 作用:触发状态修改
function reducer(state, action){
 const {type, payload} = action;
  switch(type){
    case "add":
      return {...state, count: state.count + payload}
    default:
      return state;
  }
}

action 参数作为对象,需要拥有 typepayload 属性,reducer 通过判断 type 属性对state进行不同的合并操作并返回更新后的state

createStore

createStore 返回下面几个方法:

  • getState: 获取最新state状态
  • dispatch: 触发状态更新
  • subscribe: 订阅视图更新函数

创建 createStore 支持 reducer 和 initState 作为参数:

function createStore(reducer, initState) {

    // getState
    let state = initState;
    const getState = () => state;

    // dispatch
    const dispatch = (action) => {

        if (!isPlainObject(action)) {
            throw new Error('action 必须是纯对象')
        }
        if (typeof action.type == "undefined") {
            throw new Error('必须定义action.type属性');
        }

        state = reducer(state, action);
        // 更新完state,紧接着触发订阅方法
        listeners.forEach(fn => fn());

        return action
    }
    // 初始化值
    dispatch({ type: "@@@Redux/INIT" })

    // subscribe
    const listeners = [];
    const subscribe = (listener) => {
        listeners.push(listener);
        let subscribed = true;
        return () => {
            if (!subscribed) return;
            let index = listeners.indexOf(listener);
            listeners.splice(index, 1);
            subscribed = false;
        }
    }

}

function isPlainObject(obj) {
    if (typeof obj !== "object" || obj == null) return false;

    let objPro = obj;

    // 这里拿到obj最初始的__proto__
    while (Object.getPrototypeOf(objPro)) {
        objPro = Object.getPrototypeOf(objPro);
    }

    if (objPro === Object.getPrototypeOf(obj)) return true

}

combineReducer

合并 reducer

function combineReducer(reducers) {
  let reducersKeys = Object.keys(reducers);
  return function(state={},actions){
    let combineState = {};
    for(let i=0;i<reducersKeys.length;i++){
      let key = reducersKeys[i]; // 拿到每个reducer的key值
      let reducer = reducers[key]; // 拿到每个reducer方法
      combineState[key] = reducer(state[key], actions);
    }
    return combineState;
  }
}

bindActionCreator

目的:将actions对象转换,把属性函数替换成能够执行的dispatch方法:

function bindActionCreator(actions,dispatch) {
  if(typeof actions === "function"){
    return function(){
      dispatch(actions.apply(this,arguments))
    }
  }
  let actionCreator = {};
  for(let name in actions){
    actionCreator[name] = function(){
      dispatch(actions[name].apply(this, arguments))
    }
  }
  return actionCreator;
}

适用的场景中使用 redux 可以优化代码结构,提高可读性,不适用的场景反而会让应用变得复杂,不易理解。