Redux 版本:3.7.2redux
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。设计模式
说白了Redux就是一个数据存储工具,因此数据基础模型有get方法,set方法以及数据改变后通知的对象subscribe订阅者。数组
Redux 提供了五个方法app
接下来咱们来一一解析。函数
建立一个 Redux store 来以存放应用中全部的 state。应用中应有且仅有一个 store。工具
参数:spa
返回值:设计
先来写一个基础的 createStore 以下:code
function createStore() { function getState() { } // 取 function dispatch() { } // 存 function subscribe() { } // 订阅 function replaceReducer() { } // 重置reducer return { getState, dispatch, subscribe, replaceReducer } }
getState 实现很简单,直接返回 currentState。对象
function createStore() { let currentState = {}; // 数据 function getState() { // 取 return currentState; } function dispatch() { } // 存 function subscribe() { } // 订阅 function replaceReducer() { } // 重置reducer return { getState, dispatch, subscribe, replaceReducer } }
dispatch 传入 action,经过 action.type 区别操做。
function createStore() { let currentState = {}; function getState() { // 取 return currentState; } function dispatch(action) { // 存 switch (action.type) { case 'PLUS': currentState = { ...currentState, count: currentState.count + 1, }; } return action; } function subscribe() { } // 订阅 function replaceReducer() { } // 重置reducer return { getState, dispatch, subscribe, replaceReducer } }
由于 Redux 要通用,因此 dispatch 内和业务相关的代码要提取出来,Redux 给它起了个名字,叫 reducer。
提取reducer,
const initialState = { count: 0, } export default (state = initialState, action) => { switch (action.type) { case 'PLUS': return { ...state, count: state.count + 1, } case 'MINUS': return { ...state, count: state.count - 1, } default: return state } }
给 createStore 添加两个参数 reducer, preloadedState。
preloadedState非必传,若是不传,currentState 默认值就是 undefined。
在 createStore 中添加初始化方法 dispatch({ type: '@@redux/INIT' })
; 初始化的 action.type 必须是 reducer 中没有使用过的,Redux 源码中使用了 '@@redux/INIT'
。初始化方法会执行一次 dispatch。
初始化时,若是 currentState 是 undefined, 那么在 reducer 中, state = initialState
会把 initialState 赋值给 state,而后经过 default return 出去, 最后修改 currentState。至关于 currentState = initialState。
最后 createStore 以下
function createStore(reducer, preloadedState) { let currentState = preloadedState; function getState() { // 取 return currentState; } function dispatch(action) { // 存 currentState = reducer(currentState, action); return action; } function subscribe() { } // 订阅 function replaceReducer() { } // 重置reducer dispatch({ type: '@@redux/INIT' }); // 初始化 return { getState, dispatch, subscribe, replaceReducer } }
根据代码能够看出,reducer 和 action 都是开发者自定义的,Redux 只是把 reducer 返回的 state 赋值给了 currentState,那么开发者自定义其余格式的action ,而且在 reducer 中做出对应的解析,而后返回 state,固然也是彻底能够的。只是 Redux 统一了这种写法,下降了个性化带来的开发成本。
实际上 createStore 还有第三个参数 enhancer,目前用不到,后面再讲。
subscribe 有一个参数 listener (Function): 每当 dispatch action 的时候都会执行的回调。
subscribe 使用了设计模式中的 发布-订阅模式,又叫 观察者模式。
实现:
function createStore(reducer, preloadedState) { let currentState = preloadedState; let currentListeners = []; function getState() { // 取 return currentState; } function dispatch(action) { // 存 currentState = reducer(currentState, action); currentListeners.forEach(fn => fn()); return action; } function subscribe(listener) { // 订阅 currentListeners.push(listener); } function replaceReducer() { } // 重置reducer dispatch({ type: '@@redux/INIT' }); // 初始化 return { getState, dispatch, subscribe, replaceReducer } }
重置 reducer, 并不会重置 currentState。
实现:
dispatch({ type: '@@redux/INIT' })
。注意:实际上,replaceReducer 中的 dispatch({ type: '@@redux/INIT' })
,只有此时 currentState 是 undefined 时,才有做用,会把新的 initialState 赋值给 currentState。
function createStore(reducer, preloadedState) { let currentReducer = reducer let currentState = preloadedState; let currentListeners = []; function getState() { // 取 return currentState; } function dispatch(action) { // 存 currentState = currentReducer(currentState, action); currentListeners.forEach(fn => fn()); return action; } function subscribe(listener) { // 发布订阅 currentListeners.push(listener); } function replaceReducer(nextReducer) { // 重置reducer currentReducer = nextReducer; dispatch({ type: '@@redux/INIT' }); // 重置 } dispatch({ type: '@@redux/INIT' }); // 初始化 return { getState, dispatch, subscribe, replaceReducer } }
createStore 的实现到这里已经完成,Redux 源码除此以外还作了大量的错误校验。
随着项目愈来愈大,把 reducer 放在一个文件里写会愈来愈臃肿,因而 Redux 提供了 combineReducers 方法。
先来看下如何使用
rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer}) // rootReducer 将返回以下的 state 对象 { potato: { // ... potatoes, 和一些其余由 potatoReducer 管理的 state 对象 ... }, tomato: { // ... tomatoes, 和一些其余由 tomatoReducer 管理的 state 对象,好比说 sauce 属性 ... } }
combineReducers 参数是 reducers 对象,返回一个合成后的 reducer。
实现逻辑比较简单,循环把 reducers 里的每个 reducer 都执行, 执行结果放在 nextState 里,若是数据改变了就返回 nextState,若是数据没有改变就返回传入的 state。
注意:若是数据没有改变,返回的是传入的 state,虽然此时和 nextState 数据是同样的,可是实际地址并不同。为了区分,Redux 特地用了 hasChanged 变量来记录。
function combineReducers(reducers) { const reducerKeys = Object.keys(reducers); // key[] return function combination(state = {}, action) { let hasChanged = false; // state 是否改变 const nextState = {}; // 改变后的 state // 循环 reducers reducerKeys.forEach(key => { const reducer = reducers[key]; // 当前 reducer const previousStateForKey = state[key]; // 当前 state const nextStateForKey = reducer(previousStateForKey, action); // 若是 没有匹配到action.type,会在 reducer 中的 switch default 返回传入的 state,即 previousStateForKey nextState[key] = nextStateForKey; hasChanged = hasChanged || nextStateForKey !== previousStateForKey; }) return hasChanged ? nextState : state; } }
bindActionCreators(actionCreators, dispatch) 把一个 value 为不一样 action creator 的对象,转成拥有同名 key 的对象。
action 生成器名字叫作叫 action creator, 以下
function addTodo(text) { return { type: 'ADD_TODO', text, }; }
修改数据须要这样写
dispatch(addTodo('Use Redux'))
若是咱们多个 action creator,写起来会比较繁琐,
dispatch(addTodo('Use Redux')) dispatch(plusTodo()) dispatch(setDataTodo({ id: 1 }))
因此 Redux 提供了 bindActionCreators 函数,传入 action creators 和 dispatch, 返回绑定了 dispatch 的 action creators。
实现也很简单,遍历 actionCreators, 把每一个元素用 dispatch 处理后生成新的函数,返回新函数的集合。
actionCreators 参数是 action creator 的集合对象,如 { addTodo, addTodo1 }
。实现代码以下:
function bindActionCreators(actionCreators, dispatch) { const boundActionCreators = {}; Object.keys(actionCreators).forEach(key => { const actionCreator = actionCreators[key]; if (typeof actionCreator === 'function') { boundActionCreators[key] = (...args) => dispatch(actionCreator(...args)); } }) return boundActionCreators; }
使用 bindActionCreators 写起来就会方便不少
const boundActionCreators = bindActionCreators({ addTodo, plusTodo, setDataTodo, }, dispatch); // 写入数据 boundActionCreators.addTodo('Use Redux') boundActionCreators.plusTodo() boundActionCreators.addTodo({ id: 1 })
Redux 支持 actionCreators 是一个单个 action creator 的函数,因此提取公共方法。改造以下:
function bindActionCreator(actionCreator, dispatch) { return (...args) => dispatch(actionCreator(...args)); } function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } const boundActionCreators = {}; Object.keys(actionCreators).forEach(key => { const actionCreator = actionCreators[key]; if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); } }) return boundActionCreators; }
从右到左来组合多个函数。
先来看看源码:
function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }
最后一行很难理解,把它换成function写法以下
funcs.reduce(function (a, b) { return function (...args) { return a(b(...args)) } })
先看下reduce方法
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; // 从左到右为每一个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。
previousValue 上次循环的返回值
currentValue 当前循环item
因此第二次循环过程以下
// 第一次循环返回值为 function (...args) { return a(b(...args)) } // 第二次循环时,第一个参数为:第一次循环的返回值,第二个参数为:funcs 内第三个元素,用c来表示 // 第二次循环返回值为 function (...args) { return (function (...args) { return a(b(...args)) })(c(...args)) } // 整理后 function (...args) { return a(b(c(...args))) }
因此 [a, b, c, d, e]
的执行结果是 (...args) => a(b(c(d(e(...args)))))
。
因此能看出来,funcs 内函数须要知足 函数参数和函数返回值结构一致。
applyMiddleware 是把 dispatch 一层一层包装。洋葱圈模型。
先看看 createStore 的第三个参数 enhancer
function createStore(reducer, preloadedState, enhancer) { // 实现了 preloadedState 参数能够省略 if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined') { // 看起来 enhancer 是个高阶函数,返回值仍是 store creator // 能够看出 enhancer 的大概结构为 // (createStore) => (reducer, preloadedState) => createStore(educer, preloadedState) return enhancer(createStore)(reducer, preloadedState) } // 这里是其余代码 // ... }
再看看官网给的 applyMiddleware 使用例子
let store = createStore( todos, [ 'Use Redux' ], applyMiddleware(logger) )
因此 applyMiddleware 的结构应该是
(...middlewares) => (createStore) => (reducer, preloadedState) => createStore(educer, preloadedState)
因此猜出来了 applyMiddleware 的参数是函数,返回值执行屡次后仍是 createStore(educer, preloadedState)。
因此再来看官方定义就比较好理解
Middleware 可让你包装 store 的 dispatch 方法来达到你想要的目的。同时, middleware 还拥有“可组合”这一关键特性。多个 middleware 能够被组合到一块儿使用,造成 middleware 链。其中,每一个 middleware 都不须要关心链中它先后的 middleware 的任何信息。
来看 applyMiddleware 看源码, 跟着 序号看会稍微清晰点:
applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { const store = createStore(reducer, preloadedState, enhancer) let dispatch = store.dispatch let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } // 二、chain内元素结构为 (store.dispatch) => store.dispatch // 因此 middleware(middlewareAPI) 结果为 (store.dispatch) => store.dispatch // 因此 middleware 结构为 (middlewareAPI) => (store.dispatch) => store.dispatch // 即 参数 middlewares 内元素结构为 (middlewareAPI) => (store.dispatch) => store.dispatch chain = middlewares.map(middleware => middleware(middlewareAPI)) // 一、上面解释过 compose 的返回值是 (...arg) => a(b(c(...arg))), // 因此下面 dispatch = ((...arg) => a(b(c(...arg))))(store.dispatch) // 即 dispatch = a(b(c(store.dispatch))) // 因此 a、b、c 即 chain内元素 的结构须要为 (store.dispatch) => store.dispatch dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch // 这里能够看出,applyMiddleware 只包装替换了 createStore 的 dispatch } } }
如今咱们知道了 applyMiddleware 的参数结构是 (middlewareAPI) => (store.dispatch) => store.dispatch
,而后咱们来写个简单的 middleware
// 原始长这个样子 function logger(middlewareAPI) { return (dispatch) => dispatch; } // 而后 给 dispatch 包装如下,而且换个名字叫 next function logger(middlewareAPI) { return (next) => (action) => { let value = next(action); return value; }; } // 而后 加入功能 function logger(middlewareAPI) { return (next) => (action) => { // 这里的 dispatch 是 createStore 建立的。通常不用。 const { getState, dispatch } = middlewareAPI; console.log('will dispatch', action); let value = next(action); console.log('state after dispatch', getState()); // createStore 里实现的 dispatch 返回 action, // 通常会是 action 自己,除非 // 后面的 middleware 修改了它。 return value; }; }
最后再来回味下 applyMiddleware 的这几个结构
// compose ([a, b, c, d, e]) => (...args) => a(b(c(d(e(...args))))) // applyMiddleware (...middlewares) => (createStore) => (reducer, preloadedState) => createStore(educer, preloadedState) // middleware (middlewareAPI) => (dispatch) => dispatch