“整个应用的 state被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个store 中。”这是Redux官方文档给出三大原则的第一条,着重强调了单一数据源这个概念其中,单一数据源的一个重要的前提,就是有唯一一个store,而这个store,正是被我们今天要讲的核心API——createStore创建的,至于我们担心数据过于庞大的问题,之前在概览中提到的API——combineReducers正是用来解决这个问题的。
createStore.js概览
createStore可谓是整个Redux的灵魂。基本上Redux的核心功能已经全部被createStore以及createStore生成的store中,下面我们就来了解一下createStore究竟是怎么工作的,createStore的源码比较长,我们分两段贴出︿( ̄︶ ̄)︿:
这段可以看到,createStore接受3个参数:reducer, preloadedState, enhancer。reducer我们接下来会具体介绍,这里不过多赘述,第二个参数是preloadedState,它是state的初始值,实际上他并不仅仅是扮演着一个initialState的角色,如果我们第二个参数是函数类型,createStore会认为你忽略了preloadedState而传入了一个enhancer,如果我们传入了一个enhancer,createStore会返回enhancer(createStore)(reducer, preloadedState)的调用结果,这是常见高阶函数的调用方式。在这个调用中enhancer接受createStore作为参数,对createStore的能力进行增强,并返回增强后的createStore。然后再将reducer和preloadedState作为参数传给增强后的createStore,最终得到生成的store。
这段我们省略了dispatch,subscribe,getState,replaceReducer,[$$observable]: observable的代码,因为我们会在接下来去依次剖析他们,这里也可以看出,createStore的返回值是dispatch,subscribe,getState,replaceReducer,[$$observable]: observable,它们共同组成了一个store,实际上它本身只暴露出来四个方法dispatch,subscribe,getState,replaceReducer,而[$$observable]: observable是供Redux内部使用的,有兴趣的童鞋可以自己去研究(传送门),本文就不再赘述。稍后我们将会介绍这四个我们可以使用到的API。
1.action
这里插一个action,action代表的是用户的操作。redux规定action一定要包含一个type属性,且type属性也要唯一,相同的type,redux视为同一种操作,因为处理action的函数reducer只判断action中的type属性。
2. reducer
Redux中负责响应action并修改数据的角色就是reducer,reducer的本质实际上是一个函数,其函数签名为:reducer(previousState,action) => newState。可以看出,reducer 接受两个参数,previousState以及action函数返回的action对象,并返回最新的state,实际上reducer在处理previousState还需要一个特殊的非空判断,以下是一个Dome:
reducer 只是一个模式匹配的东西,真正处理数据的函数,一般是额外在别的地方写的(当然直接写在reducer中也没问题,只是不利于后期维护),在 reducer 中调用罢了。reducer 为什么叫 reducer 呢?因为 action 对象各种各样,每种对应某个 case ,但最后都汇总到 state 对象中,从多到一,这是一个减少( reduce )的过程,所以完成这个过程的函数叫 reducer。
3.getState
源码如下:
可以看出,这里定义了4个本地变量:
1、currentReducer:当前的reducer,支持用过store.replaceReducer方式动态替换reducer,为代码热替换提供了可能。
2、currentState:应用当前的状态,默认为初始化状态。
3、listeners:当前监听store的监听器
4、isDispatching:某个action是否处于分发处理过程中。
4.subscribe
在getSate之后,我们又定义了subscribe:
subscribe接收一个listener,它的作用是给store添加监听函数。nextListeners储存了整个监听函数列表。 subscribe的返回值是一个unsubscribe,是一个解绑函数,调用该解绑函数,会将已经添加的监听函数删除,该监听函数处于一个闭包之中,会一直存在,所以在解绑函数中能删除该监听函数。(由此可见redux源码设计的精巧,多处地方巧用闭包,精简了许多代码。)
你可能会奇怪,好像我们在Redux中并没有使用过subscribe方法,实际上React Redux 中的 connect方法隐式地帮我们完成了这个工作。
5.dispatch
接下来,要说的是store非常核心的一个方法,也是我们在应用中最常使用的方法dispatch。他的源码如下:
首先,我们校验了action是否为一个原生js对象,接下来为我们校验了action对象是否包含了必要的type字段。再接下来,判断当前是否处于某个action分发过程中,这个检查主要是为了避免在reducer中分发action。
在一系列检查完毕后,若均没有问题,将当前的状态和action传给当前reducer,用于生成新的state。在得到新的状态后,依次调用所有的监听器,通知状态变更。需要注意的是,我们在通知监听器变更发生时,并没有将最新的状态传给监听器。这是因为在监听器中我们可以直接调用store.getState()方法拿到最新的状态,最后,奖处理之后的action返回。
6.replaceReducer
replaceReducer是替换当前的reducer的函数,replaceReducer接受一个新的reducer,替换完成之后,会执行 dispatch({ type: ActionTypes.INIT }) ,用来初始化store的状态。官方举出了三种replaceReducer的使用场景,分别是:
1、当你的程序要进行代码分割的时候
2、当你要动态的加载不同的reducer的时候
3、当你要实现一个实时reloading机制的时候
至此,这个至关重要的“灵魂”API——createStore已经基本介绍完了,到目前为止,我们都在一个清晰的同步数据流中跑动,但是实际开发过程中,这远远不能满足我们的需求,比如我要发送一个请求,等数据返回后再更新View怎么办?这就需要我们使用Redux middleware了,下一章,将为大家介绍Redux中间件middleware,这也是我本人非常喜欢的一块代码,代码之简洁,思维之精妙真的是很令人受教,敬请期待下一章~( ̄▽ ̄~)(~ ̄▽ ̄)~。