store.[$$observable]方法: 可以理解为 store.subscribe 的一种 observable 形式的封装,功能和 store.subscribe 一致,供相应工具使用。该方法使用并不多,不再赘述。
store.dispatch方法: store 中最核心的方法,没有之一,打通了整个工作流程,其签名为 ( action ) => action。它首先调用了 reducer 参数传入的函数更新数据,随后遍历并调用了 store.subscribe 方法注册的监听事件,告知数据发生了变更。为了在使用 createStore 生成 store 时就直接生成一个初始 state 对象,这个方法内调用了一次 dispatch({ type: ActionTypes.INIT }),由于 ActionTypes.INIT 类型不存在于你定义的 reducer 中任何处理函数,因此会返回你定义的初始状态,这也是 preloadedState 参数很少使用的原因。
使用注意createStore 中直接调用 reducer 来生成数据并未额外操作数据,因此使用时需注意:
返回的 newState 要和参数 state 没有引用关系。
任何未知 action,必须返回当前状态(参数 state 获取到的状态)。若当前状态未定义,必须返回初始状态(自定义的 initState)。
isDispatching 规范了 reducer 函数中不允许使用 getState、subscribe 和 dispatch 方法。
store 并不储存所有数据,而是储存的更改数据的方法(reducer),因此生成初始数据需要内部先调用 dispatch({ type: ActionTypes.INIT }),并且在需要更新 reducer 时要调用 replaceReducer 才能更新。为保证数据实时性,更新 reducer 后需要再调用一次 dispatch({ type: ActionTypes.REPLACE }) 来更新数据。
代码技巧技巧一: 有三个形参时,实现第二个形参可以选填:
技巧二: isDispatching 规范 reducer 中不允许使用 getState、subscribe 和 dispatch 的实现方法:
关键代码在 dispatch 方法中(见下图),可以看到在开始执行 reducer 之前 isDispatching 先置为 true,一直到 reducer 全部执行完才会再设置为 false,随后只要分别在 getState、subscribe、dispatch 三个方法中判断当 isDispatching 为 true 时抛出异常。
这是很重要的一个工具方法,代码量很小,但无论从代码技巧上还是使用地位上都很重要。
Redux 中间件的作用实际是增强了 dispatch 功能。看源码最后 return { …store, dispatch} 可以知道是用增强后的 dispatch 替代了原来的 store.dispatch。下图为 applyMiddleware 在工作流中的作用。
用法根据 applyMiddleware 的调用方式再结合 createStore 中提到的 enhancer 参数总结一下用法。
const store = createStore(reducer, applyMiddleware(…middlewares))
const store = createStore(reducer, {}, applyMiddleware(…middlewares))
applyMiddleware(…middlewares)(createStore)(reducer, preloadedState)
同时还可以借助 Redux 提供的 compose 方法来使用,只需将上述用法中的 applyMiddleware(…middlewares) 替换成 compose(applyMiddleware(middleware1),…,applyMiddleware(middlewareN)) 即可。
注意一种错误用法,该用法在 createStore 中做了限制:
createStore(reducer, applyMiddleware(middleware1), applyMiddleware(middleware2), …)
applyMiddleware 源码中调用中间件的方法见下图。
其中用到了 compose 方法,这个方法是Redux的一个API,后面详细讲解,调用 compose(middleware1, middleware2, middleware3)(store.dispatch) 相当于调用 middleware1( middleware2( middleware3( store.dispatch ) ) )。
现在我们以源码里中间件的调用方式倒推 middleware 的写法。
源码中 compose(middleware1, middleware2, middleware3)(store.dispatch) 即 middleware1( middleware2( middleware3( store.dispatch ) ) ) 调用后生成了新的 dispatch,所以 middleware 函数应是 (next) => dispatch 即 (next) => (action) => action, 其中 next 是上一个 middleware 的返回值。
middleware 函数在传入 compose 之前用 middleware(middlewareAPI) 获得了 dispatch 和 getState 供中间件内部使用,因此还需要能接收这个参数,于是:
({dispatch, getState}) => (next) => (action) => action。
没错,到这里 middleware 函数就已经写完了!