Redux源码分析

Redux是React核心开发人员的开发的一个JavaScript 状态容器,提供可预测化的状态管理。

Redux短小精悍并且没有任何第三方的依赖。

源码接口

.src
├── utils                #工具函数
├── applyMiddleware.js
├── bindActionCreators.js        
├── combineReducers.js     
├── compose.js       
├── createStore.js  
└── index.js             #入口 js

index.js

对外的整个代码的入口,暴露了 createStore, combineReducers, bindActionCreators, applyMiddleware, compose几个接口给开发者。另外也会warn提示 判断redux是否被压缩。

createStore.js

redux中最重要的一个API了,它创建一个Redux store来存放应用所有的state,整个应用中有且只有一个store。

import isPlainObject from 'lodash/isPlainObject'
import $$observable from 'symbol-observable'

// 私有 action 仅供自己初始化使用
export var ActionTypes = {
  INIT: '@@redux/INIT'
}

export default function createStore(reducer, preloadedState, enhancer) {
  // 判断接受的参数个数,来指定 reducer 、 preloadedState 和 enhancer
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  // 如果 enhancer 存在并且适合合法的函数,那么调用 enhancer,并且终止当前函数执行
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    // 如果使用了第三方的库:redux-thunk 则会进入这个流程 enhancer只接受 applyMiddleware函数返回
     return enhancer(createStore)(reducer, preloadedState)
  }

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

  // 储存当前的 currentReducer
  var currentReducer = reducer
  // 储存当前的状态
  var currentState = preloadedState
  // 储存当前的监听函数列表
  var currentListeners = []
  // 储存下一个监听函数列表
  var nextListeners = currentListeners
  var isDispatching = false

// 这个函数可以根据当前监听函数的列表生成新的下一个监听函数列表引用
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  ... getState ...

  ... subscribe ...

  ... dispatch ...

  ... replaceReducer ...

  ... observable ...
    // 当store创建了,调用dispatch方法 传递type: ActionTypes.INIT
  dispatch({ type: ActionTypes.INIT })

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

函数对外暴露方法:dispatch, subscribe, getState等等方法。

  • dispatch

    函数参数传递的是action, action为一个普通的plain object对象。

    function dispatch(action) {
      if (!isPlainObject(action)) {
        throw new Error(
          'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
        )
      }
    
      // 判断 action 是否有 type{必须} 属性
      if (typeof action.type === 'undefined') {
        throw new Error(
          'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
        )
      }
    
      // 如果正在 dispatch 则抛出错误
      if (isDispatching) {
        throw new Error('Reducers may not dispatch actions.')
      }
    
      // 对抛出 error 的兼容,但是无论如何都会继续执行 isDispatching = false 的操作
      try {
        isDispatching = true
        // 使用 currentReducer 来操作传入 当前状态和action,放回处理后的状态
        currentState = currentReducer(currentState, action)
      } finally {
        isDispatching = false
      }
    
      var listeners = currentListeners = nextListeners
      for (var i = 0; i < listeners.length; i++) {
        var listener = listeners[i]
        listener()
         }
    
      return action
    }
    

    传递dispatch(action) currentState = currentReducer(currentState, action) 传入当前的state 和action 并返回一个新的state。

  • subscribe

    可以给store的状态添加订阅监听函数,一旦调用 ' dispatch ',所有的监听函数就会执行,本身存储了一个挡墙监听函数的列表。 调用subcribe函数传入一个函数作为参数,同时会返回一个unsubscribe函数,用来解绑当前传入的监听函数。

  • getState

    返回当前store存储的currentState

compose.js

函数式编程中的概念,接受一组函数参数,从右到左来组合多个函数,然后返回一个组合函数

applyMiddleware.js

函数的作用是组合多个中间件等等,然后返回一个函数也就是所谓的enhancer。

export default function applyMiddleware(...middlewares) {
    // args 也就是所谓的reducer, preloadedState, enhancer 
    // createStore也就是之前的createStore函数 只是加上了它基础上加上了第三方的使用
  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.`
      )
    }
    let chain = []
    // 暴露getState dispatch给第三方中间件使用
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

这段代码写的很巧妙:在之前的createStore() 中

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

  return enhancer(createStore)(reducer, preloadedState)
}

所以如果有第三方库,enhancer(createStore)(reducer, preloadedState),接着调用applyMiddleware,所以此处相当于调用了 var store = createStore(reducer, preloadedState, enhancer ) ; 接着会重新生成一个新的dispatch, 首先保存之前的store.dispatch。

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

对所有的第三方中间件都暴露上面两个方法, 同时改造dispatch,使用compose来使第三方中间件从右向左依次激活。

CombineReducers

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]
    //......
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

 return 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)
      
      // ...
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    return hasChanged ? nextState : state
  }
}

首先根据key 生成一个新的finalReducers,finalReducerKeys,然后返回一个新的 combination函数(),新的reducer函数 拿到之前的state状态,然后接着调用reducer函数计算出接下来的state。

 // 获取之前的state的状态
 const previousStateForKey = state[key]
 // 计算获取action的接下来的状态
 const nextStateForKey = reducer(previousStateForKey, action)

通过hasChanged变量来判断是否是返回一个新的nextState对象还是之前就的state 判断的标准也就是所谓的 === 三个等号对引用值的判断。

你可能感兴趣的:(Redux源码分析)