作者: HerryLo
本文永久有效链接: https://github.com/AttemptWeb......
Redux是JavaScript状态容器,提供可预测化的状态管理。
在实际开发中,常搭配React + React-redux使用。这代表了目前前端开发的一个基本理念,数据和视图的分离。
redux应运而生,当然还有其他的一些状态管理库,如Flux、Elm等,当然,我们这里只对redux进行解析。
#redux创建Store
创建redux的store对象,需要调用combineReducers和createStore函数,下面解释不包含中间件。
const reducer = combineReducers({ home: homeNumber, number: addNumber }) const store = createStore(reducer) // 暂时挂载在window下,下面会使用到 window.$reduxStore = store
#combineReducers函数
首先调用combineReducers函数,将多个reducer函数作为参数传入,源码如下:
// reducers即是传入的参数对象 function combineReducers(reducers) { // ......省略 return function combination(state = {}, action) { let hasChanged = false const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { // finalReducerKeys 是传入reducers对象的key值 const key = finalReducerKeys[i] // finalReducers 等价于 reducers const reducer = finalReducers[key] const previousStateForKey = state[key] // 运行reducer函数,返回一个state // 核心:调用combination函数,实际就是循环调用传入的reducer函数 const nextStateForKey = reducer(previousStateForKey, action) nextState[key] = nextStateForKey // hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length // 返回state对象 return nextState } } // 源码地址:https://github.com/reduxjs/redux/blob/master/src/combineReducers.ts#L139
上面的代码其实非常简单,combineReducers函数运行,返回一个新的combination函数。combination函数的主要作用是返回一个挂载全部state的对象。 当combination函数被调用时,实际就是循环调用传入的reducer函数,返回state对象。将combination函数作为参数传入到createStore函数中。
#createStore函数
function createStore(reducer, preloadedState, enhancer) { // reducer --> combination函数 let currentReducer = reducer // 全部的state属性,挂载在currentState上 let currentState = preloadedState // 下面的中间件会用到 if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { // 第二个参数是一个函数,没有第三个参数的情况 enhancer = preloadedState // 将preloadedState重置 preloadedState = undefined } if (typeof enhancer !== 'undefined') { // 存在中间件时,将createStore传入中间件函数,调用enhancer函数,return结束。 return enhancer(createStore)(reducer, preloadedState) } function dispatch(action) { // currentReducer --> combination函数 currentState = currentReducer(currentState, action) } // 初始化调用dispatch,创建初始state dispatch({ type: ActionTypes.INIT }) const store = ({ dispatch: dispatch, subscribe,s getState, replaceReducer, [$$observable]: observable } return store } // 源码地址:https://github.com/reduxjs/redux/blob/master/src/createStore.ts#L60
reducer
就是传入的combination函数,preloadedState是初始化的state(没有太大的作用),enhancer是中间件,没有第三个参数enhancer的情况下,同时第二个参数preloadedState是一个函数,preloadedState被赋值给enhancer。
调用dispatch
函数初始化,currentReducer
即是传入combination
函数,就向上文提到的,调用combination
函数实际就是循环调用reducer函数。所有的state对象,被挂载在内部变量currentState
上。存在中间件enhancer时,将createStore传入中间件函数,调用enhancer函数,return结束,这个下文会继续讲到。
创建的store对象,暴露出的方法如下:
const store = ({ // 分发 action,这是触发 state 变化的惟一途径。 dispatch: dispatch as Dispatch<A>, // 变化监听器 subscribe, // 获取store下的 全部state getState, // 替换 store 当前用来计算 state 的 reducer replaceReducer } return store
dispatch
函数触发action,调用reducer函数,修改state。subscribe
函数可以监听变化state的变化。getState
函数获取全部state。replaceReducer
函数替换用来计算state的reducer
函数。
通过combineReducers
函数合并reducer函数,返回一个新的函数combination
(这个函数负责循环遍历运行reducer函数,返回全部state)。将这个新函数作为参数传入createStore
函数,函数内部通过dispatch,初始化运行传入的combination
,state生成,返回store对象
#redux中间件
最好把上面看懂之后,再看中间件部分!!下面对中间件进行分析:
redux-thunk
只是redux中间件的一种,也是比较常见的中间件。redux-thunk
库允许你编写与store交互的异步逻辑。
import thunkMiddleware from 'redux-thunk' const reducer = combineReducers({ home: homeNumber, number: addNumber }) const store = createStore( reducer, applyMiddleware( thunkMiddleware, // 异步支持 ) )
createStore函数支持三个参数,如果第二个参数preloadedState是一个函数,而没有第三个参数enhancer的话,preloadedState会被赋值给enhancer。
下面会以redux-thunk
中间件作为例子,下面就是thunkMiddleware函数的代码:
// 部分转为ES5代码,运行middleware函数会返回一个新的函数,如下: return ({ dispatch, getState }) => { // next实际就是传入的dispatch return function (next) { return function (action) { // redux-thunk核心 if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; }; } // 源码地址:https://github.com/reduxjs/redux-thunk/blob/master/src/index.js
redux-thunk
库内部源码非常的简单,github: redux-thunk 源码,允许action是一个函数,同时支持参数传递,否则调用方法不变。
#applyMiddleware函数
// 中间件调用 return enhancer(createStore)(reducer, preloadedState) 等价于 return applyMiddleware( thunkMiddleware, )(createStore)(reducer, preloadedState)
redux的中间件,从applyMiddleware函数开始,它主要的目的就是为了处理store的dispatch函数
。
// 支持多个中间件传入 export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, ...args) => { // 创建 store const store = createStore(reducer, ...args) const middlewareAPI = { getState: store.getState, dispatch: (action, ...args) => dispatch(action, ...args) } // 遍历运行中间件函数,将middlewareAPI作为参数传入 // middleware对应上面的redux-thunk库核心代码,当然也支持多个中间件 const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 核心:将所有中间件传入到compose中,返回一个新的dispatch dispatch = compose(...chain)(store.dispatch) // 照常返回一个store对象,dispatch已经被处理过了 return { ...store, dispatch } } } // 源码地址:https://github.com/reduxjs/redux/blob/master/src/applyMiddleware.ts#L55
applyMiddleware
函数接收多个middlewares参数,返回一个store对象。通过createStore创建store对象,middlewareAPI对象挂载getState和dispatch,循环middlewares中间件,将middlewareAPI作为参数传入每个中间件。遍历结束以后,拿到了一个包含所有中间件新返回函数的一个数组,将其赋值给变量chain。
// 遍历之后chain的值,这里只是拿redux-thunk库作为例子 // next 就是 dispatch chain = [function (next) { return function (action) { if (typeof action === 'function') { // redux-thunk核心 return action(dispatch, getState, extraArgument); } return next(action); }; }, ...更多中间件]
#compose函数
// 数组chain 保存所有中间件新返回函数 dispatch = compose(...chain)(store.dispatch)
compose的主要作用就是运行所有中间件函数后,返回一个经过处理的dispatch函数。
// compose函数 return chain.reduce((a, b) =>{ return (...args)=> { return a(b(...args)) } }
chain是保存中间件函数的数组,具体的内部结构参见上面👆,下面来分析一下compose函数的调用逻辑。
// chain 类比为 [fn1, fn2, fn3, fn4] [fn1, fn2, fn3, fn4].reduce((a, b) =>{ return (...args)=> { return a(b(...args)) } }
调用过程如下:
循环 | a值 | b值 | 返回的值 |
---|---|---|---|
第一轮循环 | fn1 | fn2 | (...args)=> fn1(fn2(...args)) |
第二轮循环 | (...args)=> fn1(fn2(...args)) | fn3 | (...args)=> fn1(fn2(fn3(...args))) |
第三轮循环 | (...args)=> fn1(fn2(fn3(...args))) | fn4 | (...args)=> fn1(fn2(fn3(fn4(...args)))) |
经过 compose 处理过之后, 最后的返回值就是 (...args) => fn1(fn2(fn3(fn4(...args))))
,这个的arg就是 store.dispatch函数。最后将返回函数赋值给dispatch,就是我们需要的dispatch函数了。而如果只有一个中间件的话,就会直接返回了。
applyMiddleware函数中间件的主要目的就是修改dispatch函数,返回经过中间件处理的新的dispatch函数
#redux使用
这里的使用是不配合react-redux+react的;
window.$reduxStore = store store.dispatch(action); let { aState } = store.getState()
上面是直接将其挂载在window对象之下,这样可以配合任何前端框架使用,当然这样肯定是不优雅的,后面我再会专门讲一篇配合react-redux使用的;
在这里调用store.dispatch函数,实际就是再次调用循环遍历调用reducer函数,更新之后被保存在createStore函数的内部变量currentState上。通过store.getState函数,返回currentState变量,即可得到所有state。
#结束:
内容有点多,需要总结一下
1.redux创建Store:通过combineReducers
函数合并reducer函数,返回一个新的函数combination
(这个函数负责循环遍历运行reducer函数,返回全部state)。将这个新函数作为参数传入createStore
函数,函数内部通过dispatch,初始化运行传入的combination
,state生成,返回store对象。
2.redux中间件:applyMiddleware函数中间件的主要目的就是修改dispatch函数,返回经过中间件处理的新的dispatch函数
3.redux使用:实际就是再次调用循环遍历调用reducer函数,更新state
解析到这里就结束了,redux真的非常函数化,满满的函数式编程的思想,非常的模块化,具有很强的通用性,觉得非常赞👍👍
ps: 微信公众号:Yopai,有兴趣的可以关注,每周不定期更新,分享可以增加世界的快乐