蘑菇小姐会开花

redux源码解读

redux是一个应用数据流框架,它对JavaScript应用而言是一个可预测状态的容器。redux的主要作用是应用状态的管理和数据流的处理。

源码结构

1
2
3
4
5
6
7
8
src
|------- utils #工具函数
|------- applyMiddleware.js
|------- bindActionCreators.js
|------- combineReducers.js
|------- compose.js
|------- createStore.js
------- index.js #入口文件

index.js

入口文件,export createStorecombineReducersbindActionCreatorsapplyMiddlewarecompose 这几个API

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
import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes'

function isCrushed() {}

if (
process.env.NODE_ENV !== 'production' &&
typeof isCrushed.name === 'string' &&
isCrushed.name !== 'isCrushed'
) {
warning(
'You are currently using minified code outside of NODE_ENV === "production". ' +
'This means that you are running a slower development build of Redux. ' +
'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' +
'or setting mode to production in webpack (https://webpack.js.org/concepts/mode/) ' +
'to ensure you have the correct code for your production build.'
)
}

export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes
}

isCrushed这个函数是用来检查在非生产环境下,redux是否被压缩。(默认情况下isCrushed.name === ‘isCrushed’, 被压缩后isCrushed.name会被修改)

createStore.js

createStore会生成一个store,用来维护一个全局的state树。
接受三个参数:
1.reducer 一个函数,接受当前的state和action为参数,返回next state
2.preloadedState 初始的state(可选参数)
3.enhancer store enhancer,中间件(可选)
返回一个对象store,有dispatchgetStatesubscribereplaceReducer[$$observable]: observable方法,能取到对应的state,能dispatch actions,改变store中数据的唯一方法是调用 dispatch(),也能订阅对应的变化。

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
import $$observable from 'symbol-observable'

import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject'


export default function createStore(reducer, preloadedState, enhancer) {
// 判断接受的参数哪个是preloadedState和enhancer
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
// 如果enhancer存在且是一个合法的函数,直接返回并执行该函数
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}

return enhancer(createStore)(reducer, preloadedState)
}

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

// 存储当前的reducer
let currentReducer = reducer
// 存储当前的state
let currentState = preloadedState
// 存储当前的监听函数
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false

// 获取最新的监听函数存储到nextListeners
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}

// ...getState...

// ...subscribe...

// ...dispatch...

// ...replaceReducer...
/*
替换当前的reducer,替换完要dispatch一个replace的action,来重新初始化store
官方给出的应用场景
1.代码拆分
2.动态加载部分reducers
3.实现一个热重载机制
*/
// ...observable...

// 初始化state
dispatch({ type: ActionTypes.INIT })

return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}

combineReducers

检查reducers和state是否为一一对应关系,然后遍历传入的reducers,根据不同的key,reducers[key](state[key], action)生成对应key的state,最后返回整个state。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 核心代码
// 返回一个combination函数,是最后的rootReducer(接受state,action作参数,返回新的state)
function combination(state = {}, action) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}

bindActionCreators

将actions和dispatch结合在一起,将store.dispatch(MyActionCreators.doSomething())简化为MyActionCreators.doSomething()
接受两个参数:
1.actionCreators(对象/方法)
2.dispatch
返回一个对象/方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
export default function bindActionCreators(actionCreators, dispatch) {
// 传入单个actionCreator

if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}

const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}

compose

compose(f, g, h) 柯里化为(..args) => f(g(h(...args)))

1
2
3
4
5
6
7
8
9
10
11
export default 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)))
}

applyMiddleware

接受一组中间件为参数,返回一个以createStore为参数的函数。
中间件将最重要的两个方法 getState/dispatch整合出来,并传递给中间件使用,中间件处理完之后,返回一个新的dispatch。
applyMiddleware把中间件放在一个chain数组中,通过compose方法,让中间件依次执行。每个中间件接受一个{dispatch, getState}为参数,返回一个dispatch => { retun function(action) { ... } }

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
// enhancer = applyMiddleware(...middleWare)
// 即enhancer为下面返回的函数
// 在createStore中调用enhancer(createStore)(reducer, preloadedState)即可返回对应的store
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}

const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)

return {
...store,
dispatch
}
}
}

总结

redux工作流程:
1.设计全局的state数据结构状态树
2.设计更改state数据、状态的actionTypes常量
3.根据actionTypes编写actionCreators
4.根据actionCreators编写进行数据处理的reducers
5.使用createStore(reducers)生成store
6.用bindActionCreators将actionCreators和store.dispatch绑定起来,得到一组能更改state的函数
7.分发使用各个状态修改函数dispatch

坚持原创技术分享,您的支持将鼓励我继续创作!