Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

redux源码解读 #2

Open
SunShinewyf opened this issue May 5, 2017 · 0 comments
Open

redux源码解读 #2

SunShinewyf opened this issue May 5, 2017 · 0 comments

Comments

@SunShinewyf
Copy link
Owner

SunShinewyf commented May 5, 2017

redux虽然强大,但是它的源码确实很简单


createStore

createStore返回的是一个对象,并且这个对象有如下成员函数dispatch,subscribe,getState,replaceReducerobservable

下面看它的核心源码(已经去掉一些对参数的判断):

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,
  • storedispatch作为参数传入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经过处理后pushchain数组
  • composechain数组进行处理
    先来看看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

至此,完结

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant