redux实用于很多场景,须要用到全局存储状态的利用都能够用到它,不论是换肤的利用还是购物车的场景,须要将不同的组件通过雷同的状态关联起来,或者雷同状态的变动触发不同视图的更新,都很适宜用到redux。

这篇文章通过实现一个简略的redux,了解redux是怎么把状态和视图关联起来的,接下来会实现redux这几个接口:

export {   createStore, // 创立store,承受reducer函数和初始化的状态state  combineReducer, // 合并多个reducer  bindActionCreator, // 转换action对象}

redux会有一个寄存和治理状态的函数reducer,用户更改状态需通过dispatch能力批改外面的状态,这样能防止用户间接批改state:

import * as TYPES from '../typings'function reducer(state,action){  switch(action.type){    case TYPES.ADD:      return {...state,action.payload}    default:      return state;  }}
这里能够看出,action是一个对象,而且必须有type属性,reducer通过判断type属性对state进行不同的合并操作并返回更新后的state。另外,这里还通过typings文件维护不同的type变量。

接下来是创立createStore函数,store会向外抛出getStatedispatchsubscribe这三个接口,不便用户调用:

export default function createStore(reducer,initState){  return {    getState, // 获取最新state状态    dispatch, // 触发状态更新,承受参数对象{type}    subscribe, // 订阅视图更新函数  }}

针对这个指标,咱们就开始编写外部函数,首先是创立作用域内的state,将初始值initState赋值给它,接着getState函数当然就是返回它了:

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

dispatch办法须要接管一个带有type属性的对象action,不便reducer函数调用:

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

这里看源码的时候还会判断action是否为纯对象,这里附上isPlainObject的实现:

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  }

接着是实现subscribe函数,不便用户更新视图时调用:

 let 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;      }  }

而后dispatch函数外面,每次更新完state再执行下listeners外面寄存的订阅办法:

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

三个函数实现后,根本就实现了,不过咱们还须要在函数外部执行下dispatch办法,初始化用户传过来的state值:

dispatch({ type: "@@@Redux/INIT"})

ok,这样这个简略版的createStore函数就算实现了。

接下来,实现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;  }}

combineReducer的实现形式有很多,查阅材料的时候也看到有其它的版本,不过咱们只有了解最终返回的值,是一个合并后的大reducer,照着reducer的参数和返回值来写就好了解了。

还有最初一个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就实现了,redux的思维在很多场景中都能够应用到,不过我感觉要害还在于了解什么时候须要应用它,实用的场景中应用能够优化代码构造,进步可读性,不实用的场景反而会让利用变得复杂,不易了解。