持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情
createStore
使用
实现之前先分析如何使用
// 实例化
const store = createStore(reducer, preloadedState, enhancer)
store.dispatch(action);
store.subscribe(() => {
// 获取store对象中存储的状态
const allState = store.getState()
})
综上createStore 须有三个实例方法 subscribe, dispatch, getState
回顾使用方法。
实现
1. subscribe
subscribe 其实就是一个收集方法的过程,收集所有调用subscribe传进去的回调函数
let currentListeners = []
//store.subscribe(callback)
function subscribe (listener) {
currentListeners.push(listener);
}
2. dispatch
dispatch后相当于把action发送给reducer,由reducer来根据action.type 处理不同的逻辑,最后返回最新的state,同时调用收集到的listener()
function dispatch(action){
currentState = reducer(currentState, action)
// 遍历所有的订阅者,并调用回调函数。
for (let i = 0; i < currentListeners.length; i++) {
// const listener = currentListeners[i]
// listener()
// 省略为下面写法
currentListeners[i]()
}
}
3. getState
getState 也就是获取所有状态,dispatch方法调用reducer获取到的state 我们直接赋值给createStore方法中的变量currentState,调用getState()直接将currentState变量返回即可
function getState(state) {
return currentState
}
增加类型判断逻辑后最终createStore代码如下
function createStore (reducer, preloadedState, enhancer) {
// reducer 类型判断
if (typeof reducer !== 'function') {
throw new Error(
`Expected the root reducer to be a function. Instead, received: '${kindOf(
reducer
)}'`
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('enhancer必须是函数')
}
return enhancer(createStore)(reducer, preloadedState);
}
var currentState = preloadedState;
var currentListeners = [];
function getState () {
return currentState;
}
function dispatch (action) {
// 判断action是否是一个对象
if (!isPlainObject(action)) {
throw new Error('action为对象');
}
if (typeof action.type === 'undefined') {
throw new Error('action对象中必须有type属性');
}
// 调用reducer函数 处理状态
currentState = reducer(currentState, action);
// 调用订阅的回调函数,通知订阅者
for (var i = 0; i < currentListeners.length; i++) {
var listener = currentListeners[i];
listener();
}
}
function subscribe (listener) {
currentListeners.push(listener);
}
// 默认调用一次dispatch方法 存储初始状态,同时也是在初始化的时候提前抛出以上校验的错误
dispatch({type: 'initAction'})
return {
getState,
dispatch,
subscribe
}
}
applyMiddleware
使用
applyMiddleware 就是注册中间件。
const store = createStore(rootReducer, applyMiddleware(logger, sagaMiddleware))
这里我们可能有疑问
- createStore实例化的时候第二个参数是preloadedState而不是Middleware?
- 多个中间件调用顺序?
问题1 我们查看createStore源码可以看到一下逻辑,
如果第二个参数为方法,并且没有第三个参数,我们就会把第二个参数赋值给enhancer,第二个参数当undefined
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
问题二 倒序调用。
回顾 首先createStore中参数
enhancer = applyMiddleware(logger, sagaMiddleware)
然后createStore中执行enhancer 相当于执行了
enhancer(createStore)(reducer, preloadedState)
等价于下面代码
applyMiddleware(logger, sagaMiddleware)(createStore)(reducer, preloadedState)
中间件的定义
export default store => next => action => {
console.log(store.getState(), action)
next(action)
}
createStore遇到enhancer直接返回。所以applyMiddleware 首先创建store
实现
/**
* applyMiddleware(logger, sagaMiddleware)(createStore)(reducer, preloadedState)
*
* export default store => next => action => {
* console.log(store.getState(), action)
* next(action)
* }
* */
function applyMiddleware (...middlewares) {
return function (createStore) {
// 所以最终相当于执行了下面函数
return function (reducer, preloaded) {
const store = createStore(reducer, preloaded)
/*
* 执行中间件的第一层后剩余
* next => action => {
* console.log(store.getState(), action)
* next(action)
* }
* */
const chain = middlewares.map(middleware => middleware(store));
// 执行后面两层要传递next === dispatch 第三层直接传action,相当于直接调dispatch(action)
const dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
}
}
}
}
function compose () {
const functionArray = [...arguments];
return function (dispatch) {
for (let i = functionArray.length - 1; i >= 0; i--) {
// 每次调用第二层后
dispatch = functionArray[i](dispatch);
}
return dispatch;
}
}
bindActionCreators
使用
实际就是将对象{type: "increment"} 转化为dispatch({type: "increment"})
const actions = bindActionCreators({increment, decrement}, store.dispatch);
function increment () {
return {type: "increment"}
}
function decrement () {
return {type: "decrement"};
}
实现
function bindActionCreators (actionCreators, dispatch) {
let boundActionCreators = {};
for (let key in actionCreators) {
// 之所以用自执行函数而不是直接赋值给boundActionCreators对象,
//是因为for循环定义完成后,在执行的时候key是确定的,
// 相当于所有的action最终都是最后执行的action,所以用自执行函数保持key
(function (key) {
boundActionCreators[key] = function () {
dispatch(actionCreators[key]())
}
})(key)
}
return boundActionCreators;
}
combineReducers
使用
counterReducer counter里面的逻辑函数
modalReducer modal弹框的逻辑函数
combineReducers是以key:function形式为对象作为参数
const rootReducer = combineReducers({counter: counterReducer,modal: modalReducer})
所以我们要做的就是把两个function合并成一个function,自然返回的state根据上面的key变成打的对象newState{ counter: {count: 1}, modal: {modalVisible: false} }
实现
function combineReducers (reducers) {
const reducerKeys = Object.keys(reducers);
for (let i = 0; i < reducerKeys.length; i++) {
// 获取reducer的逻辑处理函数,并判断是否为函数
let key = reducerKeys[i];
if (typeof reducers[key] !== 'function') {
throw new Error('reducer必须是函数');
}
return function (state, action) {
var nextState = {};
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i];
// couterReducer modalReducer
var reducer = reducers[key];
var previousStateForKey = state[key];
nextState[key] = reducer(previousStateForKey, action)
}
return nextState;
}
}
总结,redux代码特别是中间件的思想还是很值得学习的。