issue-blog
issue-blog copied to clipboard
redux源码解读
redux虽然强大,但是它的源码确实很简单
createStore
createStore返回的是一个对象,并且这个对象有如下成员函数dispatch,subscribe,getState,replaceReducer
和observable
下面看它的核心源码(已经去掉一些对参数的判断):
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)
}
这一段是通过传入的参数类型来做判断的如果只传入两个参数,并且第二个参数是函数而不是对象的时候,此时直接进行替换赋值。并且当检测到第三个参数enhancer
不为空并且符合规范的时候,直接执行
enhancer(createStore)(reducer, preloadedState)
这一段其实是为之后的中间件进行服务的
然后是定义了一系列变量:currentState,currentListener,nextListener
等。然后就开始依次实现store
暴露的几个函数
getState函数很简单,只是返回currentState
而已
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('Expected listener to be a function.')
}
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
subscribe函数的实现是通过nextListener
数组来实现对当前listeners
来进行增删,之所有需要一个currentListener
又需要一个nextListener
,是因为如果直接在currentListener
上进行修改,会导致由于数组是复杂类型,一旦进行更改会使整个数组发生改变。
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
上面是dispatch
的核心源码,去掉了一些类型检测,这个函数主要是通过设置了一个isDispatching
标志位,来判断传入的action
是否已经被监听过,否则则直接调用reducer
函数,并且将listener
数组都执行一遍
再继续往下看,replaceReducer
也很简单,略过
combineReducers
这部分的代码很长,但是大部分都是在对参数做一些校验处理,核心源码是下面几行
const finalReducerKeys = Object.keys(finalReducers)
...
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
}
主要的过程如下:
- 检验传递进来的参数
reducer
是否是合法的,然后将合法的reducer
放进finalReducer
中,并获取对应的key
值 - 通过一个for循环,实现对应的
reducer
获取对应key
值的state
- 返回改变过的
state
通过上面的实现,那么下面两种写法也是等价的
第一种:
function todoApp(state = {}, action) {
return {
visibilityFilter: visibilityFilter(state.visibilityFilter, action),
todos: todos(state.todos, action)
}
}
第二种:
const todoApp = combineReducers({
visibilityFilter,
todos
})
bindActionCreators
这个函数的作用,主要是弱化了store.dispatch
的作用,直接在bindActionCreators
中进行封装了,源码如下:
function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args))
}
export default function bindActionCreators(actionCreators, dispatch) {
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
}
- 首先是判断传入的
actionCreators
这个参数是function
还是一系列function
数组 - 如果是
function
,那就直接执行bindActionCreator
,可以看到bindActionCreator
中实现了dispatch
的功能 - 如果传入的是数组,则通过for循环对每一个进行上一步的操作
作用:自动把
action
创建函数绑定到dispatch
中,省略通过store.dispatch()
进行手动dispatch
applyMiddleware
这是唯一一个很绕的API,虽然代码很简洁,下面对它进行分析:
export default function 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 = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
- 首先通过调用
createStore
来生成一个store
,并且获取到dispatch
,定义了一个空数组chain
, - 将
store
和dispatch
作为参数传入middleware
中,先来看看中间件的统一格式:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
上面这个格式是采用了ES6的语法格式,解析应该是下面这样:
return function (next) {
return function (action) {
return next(action);
}
}
}
-
middleware
经过处理后push
到chain
数组 -
compose
对chain
数组进行处理 先来看看compose
的实现源码:
export default function compose(...funcs) {
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
compose底层调用的是Array.prototype.reduceRight
举例:
Dispatch = compose([fn1,fn2,fn3])(store.dispatch)
会被解析成如下所示:
dispatch = f1(f2(f3(store.dispatch))
,也就是f3的返回值作为f2的参数,f2返回值作为f1的参数
- 通过
compose
处理后返回return next(action)
其实是如下:
function(action){
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
}
这个也是包装后的store.dispatch
,与原先的store.dispatch
不同,通过这种方式一直进行传递
为什么
middlewareApi
中要使用这一行dispatch: (action) => dispatch(action)
,而不是直接dispatch:dispatch
因为store.dispatch
是一直在改变的,并且需要获取到最新的store.dispatch
,这一句正是实现这个功能,每次都可以获取最新的,而不是最先的那个store.dispatch
至此,完结