redux适用于不少场景,须要用到全局存储状态的应用均可以用到它,无论是换肤的应用仍是购物车的场景,须要将不一样的组件经过相同的状态关联起来,或者相同状态的变化触发不一样视图的更新,都很适合用到redux。
这篇文章经过实现一个简单的redux,理解redux是怎样把状态和视图关联起来的,接下来会实现redux这几个接口:redux
export { createStore, // 建立store,接受reducer函数和初始化的状态state combineReducer, // 合并多个reducer bindActionCreator, // 转换action对象 }
redux会有一个存放和管理状态的函数reducer
,用户更改状态需经过dispatch
才能修改里面的状态,这样能避免用户直接修改state:app
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会向外抛出getState
、dispatch
、subscribe
这三个接口,方便用户调用:函数
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
函数调用:this
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
的实现:spa
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
函数,方便用户更新视图时调用:code
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
的思想在不少场景中均可以使用到,不过我以为关键还在于理解何时须要使用它,适用的场景中使用能够优化代码结构,提升可读性,不适用的场景反而会让应用变得复杂,不易理解。