redux源码解析

栏目: 服务器 · 发布时间: 5年前

内容简介:关于redux的基本概念和工作流如何进行的这里就不进行过多概述了,可以查看相关文档去了解。

1.前言

关于redux的基本概念和工作流如何进行的这里就不进行过多概述了,可以查看相关文档去了解。

流程图链接

redux源码解析

2.redux源码结构

以下是redux的源码结构图,主要的就是以下几个文件组成,我们接下来按顺序进行介绍其中原理和实现过程。

redux源码解析

3.createStore.js

首先了解下createStore.js。通过调用createStore创建唯一的store,store中暴露出getState,dispatch,subscribe,replaceReducer这几个方法。通常我们用到的主要是前三个方法,这里作为主要介绍内容。如下是createStore的主要内容:

export function createStore(reducer, preloadedState, enhancer) {
  /**
   * 以下的判断都是对传入的参数进行验证
   */
  if(
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function') 
  ) {
    throw new Error('只能传递一个enhancer到createStore()中')
  }

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

  if(typeof enhancer !== 'undefined') {
    if(typeof enhancer !== 'function') {
      throw new Error('enhancer应该为一个函数')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }

  if(typeof reducer !== 'function') {
    throw new Error('reducer应该为一个函数')
  }

  /**
   * 初始化参数
   */
  let currentReducer = reducer //初始化reducer
  let currentState = preloadedState //初始化state
  let currentListeners = [] //初始化subscribe监听函数数组
  let nextListeners = currentListeners 
  let isDispatching = false

  /**
   * 复制一份currentListeners,为了防止在dispatch的时候
   * 调用subscribe和unsubscribe时候发生错误
   */
  function ensureCanMutateNextListeners() {
    if(nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }

  /**
   * 获取当前的state
   */
  function getState() {
    if(isDispatching) {
      throw new Error('不可以在isDispatching的时候调用getState')
    }
    return currentState
  }

  /** 
   * 订阅监听事件,触发dispatch后执行
  */
  function subscribe(listener) {
    if(typeof listener != 'function') {
      throw new Error('Expected the listener to be a function.')
    }

    if(isDispatching) {
      throw new Error('isDispatching的时候无法调用')
    }

    let isSubscribed = true
    ensureCanMutateNextListeners() 
    nextListeners.push(listener)

    return function unsubscribe() {
      if(!isSubscribed) { //正在解除监听事件的时候不向下执行
        return
      }
      if(isDispatching) {
        throw new Error('正在dispatch的时候不给执行')
      }
      isSubscribed = false 
      ensureCanMutateNextListeners() 
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index)
    }
  }

  /**
   * 执行好dispatch循环调用每个subscribe的函数
   */
  function dispatch() {
    //关于验证的代码就不写了
    const listeners = (currentListeners = nextListeners)
    for(let i=0; i<listeners.length; i++) {
      listeners[i]()
    }
    return action
  }

  /**
   * 替换当前的reducer然后重新初始化一次dispatch
   */
  function replaceReducer(nextReducer) {
    currentReducer = nextReducer
    dispatch({type: '@INITACTION'})
  }

  //初始化执行dispatch
  dispatch({type: '@INITACTION'})
}

4. combineReducers.js

combineReducers,它接收多个reducer函数,并整合,归一化成一个rootReducer。其返回值rootReducer将会成为createStore的参数,完成store的创建。

combineReducers只接收一个参数,这个参数阐述了不同reducer函数和页面状态数据树不同部分的映射匹配关系。

const combineReducers = (reducers) => {
    return (state={}, action) => {
        Object.keys(reducers).reduce((nextState, key) => {
            nextState[key] = reducers[key](state[key], action)
            return nextState
        }, {})
    }
}

5. applyMiddleware.js

可以通过此方法给redux在触发action到reducer的过程中增加一个中间环节。applyMiddleware返回的内容我们称为enhancer。这个是createStore方法的最后一个参数,并且是可选的。

在redux源码中涉及中间件的脚本有applyMiddleware.js、createStore.js、compose.js。那么applyMiddleware(...middlewares)中会发生什么事情。

在createStore.js中有一段源码如下:

export default function createStore(reducer, preloadedState, enhancer) {
    //...
    return enhancer(createStore)(reducer, preloadedState)
    //...
}

顾名思义,applyMiddleware就是对各个需要的中间件进行糅合,并作为createStore的第二个或者第三个参数传入。用于增强store。源码如下:

const combineReducers = (reducers) => {
    return (state = {}, action) => {
        return Object.keys(reducers).reduce((nextState, key) => {
            nextState[key] = reducers[key](state[key], action)
            return nextState
        }, {})
    }
}

export default function applyMiddleware(...middlewares) {
    return (next) => {
        return (reducer, initialState) => {
            var store = next(reducer, initialState)
            var dispatch = store.dispatch
            var chain = []

            //包装一下store的getState和dispatch方法
            //是第三方中间件需要使用的参数
            
            var middlewareAPI = {
                getState: store.getState,
                dispatch: (action) => dispatch(action)
            }
            //每个中间件也是一个高度柯里化的函数,它接收middlewareAPI参数后的第一次返回结果并存储到chain数组中
            //chain数组中每一项都是对dispatch的增强,并进行控制权转移。
            chain = middlewares.map(middleware => middleware(middlewareAPI))
            //这里的dispatch函数就是增强后的dispatch,因此compose方法接收了chain数组和原始dispatch方法。
            dispatch = compose(...chain, store.dispatch)
            return {
                ...store,
                dispatch
            }
        }
    }
}

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)))
}

6. compose.js

这个方法在applymiddleware中介绍了,可以在上面看到。

7.bindActionCreators.js

这个模块涉及的内容较少,我们直接去看源码:

function bindActionCreator(actionCreator, dispatch) {
    //这个函数主要作用就是返回一个函数,当我们调用返回的这个函数的时候
    //会自动的dispatch对应的action
    return function() {
        return dispatch(actionCreator.apply(this, args))
    }
}
/**
    参数说明: 
        actionCreators: action create函数,可以是一个单函数,也可以是一个对象,这个对象的所有元素都是action create函数
        dispatch: store.dispatch方法
*/
export default function bindActionCreators(actionCreators, dispatch) {
  // 如果actionCreators是一个函数的话,就调用bindActionCreator方法对action create函数和dispatch进行绑定
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
  // actionCreators必须是函数或者对象中的一种,且不能是null
  if (typeof actionCreators !== 'object' || actionCreators === null) {
    throw new Error(
      `bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` +
      `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
    )
  }

  // 获取所有action create函数的名字
  const keys = Object.keys(actionCreators)
  // 保存dispatch和action create函数进行绑定之后的集合
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    // 排除值不是函数的action create
    if (typeof actionCreator === 'function') {
      // 进行绑定
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  // 返回绑定之后的对象
  /**
      boundActionCreators的基本形式就是
      {
      actionCreator: function() {dispatch(actionCreator.apply(this, arguments))}
      }
  */
  return boundActionCreators
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

大数据时代小数据分析

大数据时代小数据分析

屈泽中 / 电子工业出版社 / 2015-7-1 / 69.00元

《大数据时代小数据分析》是一本大数据时代下进行小数据分析的入门级教材,通过数据分析的知识点,将各类分析工具进行串联和对比,例如:在进行线性规划的时候可以选择使用Excel或LINGO或Crystal Ball。工具的应用难易结合,让读者循序渐进地学习相关工具。JMP和Mintab用来分析数据,分析的结果使用Excel、LINGO、Crystal Ball来建立数据模型,最后使用Xcelsius来动......一起来看看 《大数据时代小数据分析》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具