redux和react-redux原理分析

简介

redux 和 react-redux 是两个不同的库,redux 的出现是为了解决大型应用中组件状态管理混乱的问题,react-redux 则是便于 redux 结合 react.js 来使用。
在阅读本文之前,您需要了解 redux 及 react 的基本用法。
本文省略了源码中的一些对理解影响不大的代码,只对核心源码做分析。

redux

redux 是真正的状态管理者,其通过闭包使状态数据永久地保存在内存当中,通过闭包来改变和获取状态数据。
redux 一共导出了五个方法

1
2
3
4
5
6
7
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose
}

createStore

createStore 用来创建一个存储对象,其共有三个参数

  1. reducer 是 redux 的核心部分,reducer 的作用就是来改变数据的
    createStore 返回一个我们称之为 store 的对象,调用其 store 的 dispatch 方法会自动调用我们传入的 reducer,用 reducer 的返回值改变 state

  2. preloadedState 是初始化状态

  3. enhancer 的中文意思是增强器,其实就是一个包装函数

以下是其部分源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
function createStore(reducer, preloadedState, enhancer){
var _ref2;

if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState;
preloadedState = undefined;
}

if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.');
}

return enhancer(createStore)(reducer, preloadedState);
}
// 将reducer, preloaderState存在局部变量里,同时初始化listeners为一个数组
var currentReducer = reducer;
var currentState = preloadedState;
var currentListeners = [];
var nextListeners = currentListeners;
// 是否正在调用reducer
var isDispatching = false;

...

// 返回当前状态
function getState() {
return currentState;
}

// 添加监听函数,同时返回一个取消监听本次添加的函数
function subscribe(listener) {
var isSubscribed = true;
nextListeners.push(listener);
return function unsubscribe() {
isSubscribed = false;
var index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
};
}

// 在dispatch中调用reducer,由reducer判断action来改变当前状态
// redux约定所有的状态改变都是通过dispatch调用reducer
function dispatch(action) {
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}

// 循环调用listener
var listeners = currentListeners = nextListeners;
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
listener();
}

return action;
}

// 直接使用传入的新reducer改变当前reducer,同时触发一个REPLACE action
function replaceReducer(nextReducer) {
currentReducer = nextReducer;
dispatch({ type: ActionTypes.REPLACE });
}

function observable() {
var _ref;

var outerSubscribe = subscribe;
return _ref = {
/**
* observer : { next: function(state){} }
*/

subscribe: function subscribe(observer) {
function observeState() {
if (observer.next) {
observer.next(getState());
}
}
observeState();
// 相当于在listener中传入getState
var unsubscribe = outerSubscribe(observeState);
return { unsubscribe: unsubscribe };
}
}, _ref[result] = function () { // result只是一个独立的key
return this;
}, _ref;
}

// 最后返回
return _ref2 = {
dispatch: dispatch,
subscribe: subscribe,
getState: getState,
replaceReducer: replaceReducer
}, _ref2[result] = observable, _ref2;
}

combineReducers

这个方法接收一个 reducer 对象(key - value),返回一个函数,调用这个函数去触发所有传入的 reducer,然后改变 state,返回新的 state 集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function combineReducers(reducers) {
var reducerKeys = Object.keys(reducers)
var finalReducers = {}
// 复制到finalReducers
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
var finalReducerKeys = Object.keys(finalReducers)

// 返回一个函数,通过传入state集合和action调用
return function combination() {
var state =
arguments.length > 0 && arguments[0] !== undefined
? arguments[0]
: {}
var action = arguments[1]
var hasChanged = false
var nextState = {}
// 循环触发所有的reducer
for (var _i = 0; _i < finalReducerKeys.length; _i++) {
var _key = finalReducerKeys[_i]
var reducer = finalReducers[_key]
var previousStateForKey = state[_key]
var nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
var errorMessage = getUndefinedStateErrorMessage(_key, action)
throw new Error(errorMessage)
}
nextState[_key] = nextStateForKey
// 状态是否改变
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
// 根据是否改变了state选择返回新的state或者原来的
return hasChanged ? nextState : state
}
}

bindActionCreators

我们介绍过,dispatch 会接受一个 action,并将当前 state 和 action 传入 reducer 然后返回新的 state
bindActionCreator 接收两个参数 actionCreator,dispatch,这个方法会生成一个函数,内容就是调用 dispatch,
actionCreator 就和它名字的含义一样,就是一个专门用于生成 action 的函数,然后由 dispatch 将生成的 action 传给 reducer,再做状态的改变。
bindActionCreators 则是通过对一组 actionCreator 做这样的操作,并且将最后的函数也以 key - value 对象的形式返回,key 值就是传入 actionCreators 的 key 值一一对应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}

function bindActionCreators(actionCreators, dispatch) {
// actionCreators是函数,那么相当于bindActionCreator
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}

// 当actionCreators是一个对象的时候
var keys = Object.keys(actionCreators)
var boundActionCreators = {}
for (var i = 0; i < keys.length; i++) {
var key = keys[i]
var actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(
actionCreator,
dispatch
)
}
}
return boundActionCreators
}

applyMiddleware

middleware 顾名思义就是中间件,目的是在 dispatch 前后做一些记录之类的操作,这有点像 Koa 中的洋葱模型。
applyMiddleware 可以根据具体的用法去理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function logger({ getState }) {
return next => action => {
console.log('will dispatch', action)
const returnValue = next(action)
console.log('state after dispatch', getState())
return returnValue
}
}
// todos是个reducer
const store = createStore(todos, ['Use Redux'], applyMiddleware(logger))
// 相当于
const store = applyMiddleware(logger)(createStore)(reducer, preloadedState)
// 此时的dispatch是处理过后的dispatch
store.dispatch({ type: 'ACTION1' })
// 相当于原始dispatch
const middle = [logger(middlewareAPI)]
_dispatch = function() {
return logger(middlewareAPI)(store.dispatch)
}
// 即
(action => {
console.log('will dispatch', action)
// 此时的next就是store.dispatch
// 如果是多个中间件,那么最后一个中间件的next是store.dispatch
// 前面的中间件中的next是后一中间件, 最终action还是会传给dispatch
const returnValue = next(action)
console.log('state after dispatch', getState())
return returnValue
}
})({ type: 'ACTION1' })

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function applyMiddleware() {
...
return function(createStore) {
return function() {
for (
var _len2 = arguments.length, args = Array(_len2), _key2 = 0;
_key2 < _len2;
_key2++
) {
args[_key2] = arguments[_key2]
}

// 调用createStore正常创建store,并将参数传入,即我们上面例子中的reducer, preloadedState
var store = createStore.apply(undefined, args)
var _dispatch = function dispatch() {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}

var middlewareAPI = {
getState: store.getState,
dispatch: function dispatch() {
return _dispatch.apply(undefined, arguments)
}
}
// middlewares即我们传入的中间件组成的数组
// 循环调用middleware并传入middlewareAPI,这样我们的中间件就能取到getState了
var chain = middlewares.map(function(middleware) {
return middleware(middlewareAPI)
})
// chain是middlewares遍历执行过后的结果组成的数组
// 所以每次dispatch的时候会循环调用所有的middlewares
// 即 middleware1(middleware2(middleware3(store.dispatch)))
_dispatch = compose.apply(undefined, chain)(store.dispatch)

return _extends({}, store, {
dispatch: _dispatch
})
}
}
}

compose

传入多个 function, compose 会对这些 function 做链式调用,将后一函数执行结果传入前一函数,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const func1 = function() {
console.log(arguments)
return 1
}
const func2 = function() {
console.log(arguments)
return 2
}
const func3 = function() {
console.log(arguments)
return 3
}
const com = compose(
func1,
func2,
func3
)
com(1, 2, 3)
// 相当于
function(){
return func1(func2(func3(1,2,3)))
}

结合以下源码来分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function compose() {
// 所有函数放到一个数组
for (
var _len = arguments.length, funcs = Array(_len), _key = 0;
_key < _len;
_key++
) {
funcs[_key] = arguments[_key]
}
...
return funcs.reduce(function(a, b) {
return function() {
return a(b.apply(undefined, arguments))
}
})
}

那么 com 相当于

1
2
3
function(){
return func1(func2.apply(undefined, func3.apply(undefined, arguments)))
}

总结

createStore 创建局部对象,将我们传入的用于改变状态的 reducer 存起来,返回闭包函数组成的对象 store,强制约定取 state 通过 store.getState,改变 state 则必须通过调用 dispatch 触发 reducer 根据具体的 action 改变,同时加入中间件功能,使我们能够统一监听到状态的改变,满足更多的需求。而 combineReducers 则满足了组件模块化的需求。
redux 是一个非常精炼的库,可以说用最少的代码实现了完整的状态管理。

react-redux

react-redux 一共导出了 4 个方法

1
2
3
{
Provider, createProvider, connectAdvanced, connect
}

Provider

Provider 是 createProvider 执行返回的实例

1
var Provider = createProvider()

createProvider

createProvider 返回一个 Provider,会将我们传入的 props 放到其 childContext 中。
这一功能利用的是 react 提供的getChildContext方法,getChildContext 方法返回的对象可以在子组件中通过 this.context 取到,此方法在最新的 react 中属于遗弃方法,react 提供了最新的 Context 实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
function createProvider() {
...
// _Component是react.Component, 所以Provider继承自react.Component
var Provider = (function(_Component) {
// 继承prototype
inherits(Provider, _Component)

// getChildContext 实现之后,所有子组件都可以通过context取到返回的对象,通常就是我们传入的store
Provider.prototype.getChildContext = function getChildContext() {
var _ref
// 返回包含store的对象
return (
(_ref = {}),
(_ref[storeKey] = this[storeKey]),
(_ref[subscriptionKey] = null),
_ref
)
}

function Provider(props, context) {
// 判断Provider是否可调用
classCallCheck(this, Provider)
// 继承实例
var _this = possibleConstructorReturn(
this,
_Component.call(this, props, context)
)

// 将store存到实例里
_this[storeKey] = props.store
return _this
}

// render子组件
Provider.prototype.render = function render() {
return react.Children.only(this.props.children)
}

return Provider
})(react.Component)

...

return Provider
}

connect

connect 生成一个容器组件,将 state 或者具体的 dispatch 注入到被包裹的组件当中。
connect 基本用法:

1
2
3
4
5
6
7
8
9
10
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) }
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoApp)

connect 部分源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function connect(mapStateToProps, mapDispatchToProps, mergeProps) {
// mapStateToPropsFactories默认是
// var defaultMapDispatchToPropsFactories = [whenMapDispatchToPropsIsFunction, whenMapDispatchToPropsIsMissing, whenMapDispatchToPropsIsObject];

// 遍历执行mapStateToPropsFactories中的函数并传入mapStateToProps,返回第一个不为falsy的执行结果
var initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps');
var initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps');
var initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps');
//
return connectHOC(selectorFactory, _extends({
initMapStateToProps: initMapStateToProps,
initMapDispatchToProps: initMapDispatchToProps,
initMergeProps: initMergeProps,
})
}

mapStateToProps 是个函数 initMapStateToProps 是 whenMapDispatchToPropsIsFunction 执行过后的结果。

whenMapStateToPropsIsFunction 调用了 wrapMapToPropsFunc(mapStateToProps, ‘mapStateToProps’),需要分析一下 wrapMapToPropsFunc,它返回的是一个闭包函数,存储了传入的 mapToProps, methodName

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function wrapMapToPropsFunc(mapToProps, methodName) {
return function initProxySelector(dispatch, _ref) {
var displayName = _ref.displayName

// 顾名思义,就是个代理,stateOrDispatch与mapToProps之间的桥梁
// 前提是mapToProps必须是个函数
var proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
return proxy.dependsOnOwnProps
? proxy.mapToProps(stateOrDispatch, ownProps)
: proxy.mapToProps(stateOrDispatch)
}

// allow detectFactoryAndVerify to get ownProps
proxy.dependsOnOwnProps = true

proxy.mapToProps = function detectFactoryAndVerify(
stateOrDispatch,
ownProps
)
{

// 原本的属性mapStateToProps
proxy.mapToProps = mapToProps
// mapToProps.dependsOnOwnProps !== null && mapToProps.dependsOnOwnProps !== undefined ? Boolean(mapToProps.dependsOnOwnProps) : mapToProps.length !== 1;
proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps)
var props = proxy(stateOrDispatch, ownProps)

if (typeof props === 'function') {
proxy.mapToProps = props
proxy.dependsOnOwnProps = getDependsOnOwnProps(props)
props = proxy(stateOrDispatch, ownProps)
}

verifyPlainObject(props, displayName, methodName)

return props
}

return proxy
}
}

所以 initMapStateToProps 执行过后其实就是这个 initProxySelector 闭包。

再来看看 mapDispatchToProps,如果是个函数,那就还是走上面那套。通常这是个对象,所以 initMapDispatchToProps 是执行 whenMapDispatchToPropsIsObject 过后的结果,定义在该对象的函数都将被当作 Redux action creator,如下所示

1
2
3
4
5
6
7
function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
return mapDispatchToProps && typeof mapDispatchToProps === 'object'
? wrapMapToPropsConstant(function(dispatch) {
return redux.bindActionCreators(mapDispatchToProps, dispatch)
})
: undefined
}

那么 wrapMapToPropsConstant 返回的是如下函数:

1
2
3
4
5
6
7
8
9
10
11
12
function initConstantSelector(dispatch, options) {
var constant = (function(dispatch) {
//即上文传入的mapStateToProps
return redux.bindActionCreators(mapDispatchToProps, dispatch)
})(dispatch, options)

function constantSelector() {
return constant
}
constantSelector.dependsOnOwnProps = false
return constantSelector
}

connectHOC 可以通过 createConnect 传入,默认情况是 connectAdvanced:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// selectorFactory 也可以通过 createConnect 传入,默认是 finalPropsSelectorFactory
function connectAdvanced(selectorFactory) {
// _ref是传入的包含initMapStateToProps的对象
// 创建一个新对象复制_ref的值,但不包括第二个参数数组里的value对应key的值
connectOptions = objectWithoutProperties(_ref, [
'getDisplayName',
'methodName',
'renderCountProp',
'shouldHandleStateChanges',
'storeKey',
'withRef'
])
// connect(mapStateToProps, mapDispatchToProps)执行过后返回
return function wrapWithConnect(WrappedComponent) {
invariant_1$2(
typeof WrappedComponent == 'function',
'You must pass a component to the function returned by ' +
(methodName +
'. Instead received ' +
JSON.stringify(WrappedComponent))
)

var wrappedComponentName =
WrappedComponent.displayName || WrappedComponent.name || 'Component'

var displayName = getDisplayName(wrappedComponentName)

// 其中包含我们的initMapStateToProps
var selectorFactoryOptions = _extends({}, connectOptions, {
...//省略了一些属性
})

// 匿名函数,内部生成一个组件,然后返回
var Connect = (function(_Component) {
inherits(Connect, _Component)
// 这个组件就是我们所说的容器组件了
function Connect(props, context) {
var _this = possibleConstructorReturn(
this,
_Component.call(this, props, context)
)

// 将store存到了实例里
_this.store = props[storeKey] || context[storeKey]
_this.initSelector()
_this.initSubscription()
return _this
}

Connect.prototype.initSelector = function initSelector() {
// selectorFactory 默认是 finalPropsSelectorFactory,下面会做分析
var sourceSelector = selectorFactory(
this.store.dispatch,
selectorFactoryOptions
)
this.selector = makeSelectorStateful(sourceSelector, this.store)
this.selector.run(this.props)
}
...
})(react.Component)
...
/**
此方法将传入的WrappedComponent业务组件的属性方法,但不包括
REACT_STATICS,KNOWN_STATICS常量中的属性方法复制到Connect组件
**/

return hoistNonReactStatics(Connect, WrappedComponent)
}
}

Connect.prototype.initSelector 中调用的 selectorFactory 默认是 finalPropsSelectorFactory,如下,而传入的参数_ref2 中包含了我们上文传入的 initMapStateToProps,initMapDispatchToProps,initMergeProps,还记得吧,这三个参数都是函数,上面做过分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function finalPropsSelectorFactory(dispatch, _ref2) {
var initMapStateToProps = _ref2.initMapStateToProps,
initMapDispatchToProps = _ref2.initMapDispatchToProps,
initMergeProps = _ref2.initMergeProps,
// 重新生成一个对象,不包含这三个参数
options = objectWithoutProperties(_ref2, [
'initMapStateToProps',
'initMapDispatchToProps',
'initMergeProps'
])
//
var mapStateToProps = initMapStateToProps(dispatch, options)
// 所有redux action creators
var mapDispatchToProps = initMapDispatchToProps(dispatch, options)
var mergeProps = initMergeProps(dispatch, options)

{
verifySubselectors(
mapStateToProps,
mapDispatchToProps,
mergeProps,
options.displayName
)
}

var selectorFactory = options.pure
? pureFinalPropsSelectorFactory
: impureFinalPropsSelectorFactory

return selectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch,
options
)
}

最后来看看 impureFinalPropsSelectorFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function impureFinalPropsSelectorFactory(
mapStateToProps,
mapDispatchToProps,
mergeProps,
dispatch
)
{

return function impureFinalPropsSelector(state, ownProps) {
return mergeProps(
mapStateToProps(state, ownProps),
mapDispatchToProps(dispatch, ownProps),
ownProps
)
}
}

我们可以看到 impureFinalPropsSelector 返回的就是具体的 state 中的值,以及上文的 redux action creators 组成的 props
再回头看 Connect 中的 initSelector:

1
2
3
4
5
6
7
var sourceSelector = selectorFactory(
this.store.dispatch,
selectorFactoryOptions
)
this.selector = makeSelectorStateful(sourceSelector, this.store)
// 将传入的mapStateToProps、mapDispatchToProps与this.props合并,并放到this.selector.props中
this.selector.run(this.props)

selectorFactory 就是 impureFinalPropsSelectorFactory 的返回值。
看一下 makeSelectorStateful:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function makeSelectorStateful(sourceSelector, store) {
// wrap the selector in an object that tracks its results between runs.
var selector = {
run: function runComponentSelector(props) {
try {
// 更新注入其中的mapStateToProps、mapDispatchToProps中注入过去的值
var nextProps = sourceSelector(store.getState(), props)
if (nextProps !== selector.props || selector.error) {
// 容器组件是否更新也是取决于此
selector.shouldComponentUpdate = true
// 更新props,
selector.props = nextProps
selector.error = null
}
} catch (error) {
selector.shouldComponentUpdate = true
selector.error = error
}
}
}

return selector
}

经过这一系列的处理,我们的 mapStateToProps, mapDispatchToProps 涉及的 state 和 dispatch redux action creators 都一起放到了一个 Connect 组件的 selector.props

Connect 的 render 方法里直接重新创建一个 element 将 selector.props 放入到我们传入的实际业务组件里:

1
2
3
4
5
6
7
8
9
10
11
12
13
Connect.prototype.render = function render() {
var selector = this.selector
selector.shouldComponentUpdate = false

if (selector.error) {
throw selector.error
} else {
return react.createElement(
WrappedComponent,
this.addExtraProps(selector.props)
)
}
}

所以经过上面这些流程,我们就可以在业务组件里直接使用 this.props.stateName, this.props.dispatchName 来使用我们的方法了。

总结

react-redux 首先通过 Provider 借用 getChildContext 让子组件取到 store,然后 connect 函数中在 connectHOC 之前,通过判断 mapStateToProps/mapDispatchToProps 的类型,分别创建闭包函数,然后在 connectHOC 中,创建一个 Connect 容器组件,在这个容器组件中将之前处理过的闭包函数赋值到 Connect 的 selector,并且将 selector 具体的属性注入到子组件的 props 里,同时 setState、shouldComponentWillUpdate 等操作或者判断都在操作这个 selector,以此来完成整个组件更新。

代码中运用了大量的函数式编程,要搞清楚还是要花点时间。