博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
redux源码阅读
阅读量:7106 次
发布时间:2019-06-28

本文共 17321 字,大约阅读时间需要 57 分钟。

redux安装

初始化项目

npm init复制代码

下载redux

npm install redux --save复制代码

代码目录

redux的目录结构很简单,如下图:

源码阅读

createStore.js

/** * Creates a Redux store that holds the state tree. * The only way to change the data in the store is to call `dispatch()` on it. * * There should only be a single store in your app. To specify how different * parts of the state tree respond to actions, you may combine several reducers * into a single reducer function by using `combineReducers`. * * @param {Function} reducer A function that returns the next state tree, given * the current state tree and the action to handle. * * @param {any} [preloadedState] The initial state. You may optionally specify it * to hydrate the state from the server in universal apps, or to restore a * previously serialized user session. * If you use `combineReducers` to produce the root reducer function, this must be * an object with the same shape as `combineReducers` keys. * * @param {Function} [enhancer] The store enhancer. You may optionally specify it * to enhance the store with third-party capabilities such as middleware, * time travel, persistence, etc. The only store enhancer that ships with Redux * is `applyMiddleware()`. * * @returns {Store} A Redux store that lets you read the state, dispatch actions * and subscribe to changes. */  export default function createStore(reducer, preloadedState, enhancer) {    // 对createStore的选填参数进行判断  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {   // enhancer和preloadedState进行互换    enhancer = preloadedState    preloadedState = undefined  }    //  如果传参enhancer,那么enhancer必须是方法    //  通过enhancer把createStore包装一次,并传入reducer,preloadedState,    //  这是传了中间件的情况  if (typeof enhancer !== 'undefined') {    if (typeof enhancer !== 'function') {      throw new Error('Expected the enhancer to be a function.')    }    return enhancer(createStore)(reducer, preloadedState)  }    // 验证reducer  if (typeof reducer !== 'function') {    throw new Error('Expected the reducer to be a function.')  }  let currentReducer = reducer // 当前的reducer函数  let currentState = preloadedState // 当前状态树  let currentListeners = [] // 当前监听列表  let nextListeners = currentListeners // 当前监听列表的引用  let isDispatching = false // 是否正在分发    // 如果nextListeners和currentListeners的引用地址相同,则nextListeners拷贝一份currentListeners  function ensureCanMutateNextListeners() {    if (nextListeners === currentListeners) {      nextListeners = currentListeners.slice()    }  }  /**   * Reads the state tree managed by the store.   *   * @returns {any} The current state tree of your application.   *    */    //当isDispatching为false时,返回当前state树  function getState() {    if (isDispatching) {      throw new Error(        'You may not call store.getState() while the reducer is executing. ' +          'The reducer has already received the state as an argument. ' +          'Pass it down from the top reducer instead of reading it from the store.'      )    }    return currentState  }  // getState 方法,返回当前状态树    /**   * Adds a change listener. It will be called any time an action is dispatched,   * and some part of the state tree may potentially have changed. You may then   * call `getState()` to read the current state tree inside the callback.   *   * You may call `dispatch()` from a change listener, with the following   * caveats:   *   * 1. The subscriptions are snapshotted just before every `dispatch()` call.   * If you subscribe or unsubscribe while the listeners are being invoked, this   * will not have any effect on the `dispatch()` that is currently in progress.   * However, the next `dispatch()` call, whether nested or not, will use a more   * recent snapshot of the subscription list.   *   * 2. The listener should not expect to see all state changes, as the state   * might have been updated multiple times during a nested `dispatch()` before   * the listener is called. It is, however, guaranteed that all subscribers   * registered before the `dispatch()` started will be called with the latest   * state by the time it exits.   *   * @param {Function} listener A callback to be invoked on every dispatch.   * @returns {Function} A function to remove this change listener.   */   // 组册一个监听函数  function subscribe(listener) {  // 参数listener是一个方法而且必传    if (typeof listener !== 'function') {      throw new Error('Expected the listener to be a function.')    }    if (isDispatching) {      throw new Error(        'You may not call store.subscribe() while the reducer is executing. ' +          'If you would like to be notified after the store has been updated, subscribe from a ' +          'component and invoke store.getState() in the callback to access the latest state. ' +          'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'      )    }   // 设置私有变量isSubscribed为true    let isSubscribed = true    ensureCanMutateNextListeners()    // 将新的listener添加到nextListeners里    nextListeners.push(listener)   // 返回取消监听方法unsubscribe,通过判断判断当前监听是否存在等可注销当前监听。    return function unsubscribe() {      if (!isSubscribed) {        return      }      if (isDispatching) {        throw new Error(          'You may not unsubscribe from a store listener while the reducer is executing. ' +            'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'        )      }      isSubscribed = false      ensureCanMutateNextListeners()      const index = nextListeners.indexOf(listener)      nextListeners.splice(index, 1)    }  }    function dispatch(action) {    // 通过isPlainObject判断是否为对象    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?'      )    }    if (isDispatching) {      throw new Error('Reducers may not dispatch actions.')    }    // 通过reducer返回一个新的state座位当前的state    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()    }    // 最后返回action    return action  }    // nextReducer必须是方法,重新初始化状态树  function replaceReducer(nextReducer) {    if (typeof nextReducer !== 'function') {      throw new Error('Expected the nextReducer to be a function.')    }    currentReducer = nextReducer    dispatch({ type: ActionTypes.REPLACE })  }    // 用于观察者模式  function observable() {    const outerSubscribe = subscribe    return {      subscribe(observer) {        if (typeof observer !== 'object' || observer === null) {          throw new TypeError('Expected the observer to be an object.')        }        function observeState() {          if (observer.next) {            observer.next(getState())          }        }        observeState()        const unsubscribe = outerSubscribe(observeState)        return { unsubscribe }      },      [$$observable]() {        return this      }    }  }  dispatch({ type: ActionTypes.INIT })  return {    dispatch,    subscribe,    getState,    replaceReducer,    [$$observable]: observable  }}复制代码

createStore接受三个参数 :

reducer、preloadedState、enhancer其中reducer是必传后面两个是选传复制代码

reducer:这是一个函数,接受两个参数(stsate和action),reducer在dispatch中被调用。

preloadedState: 初始的state。

enhancer: enhancer也是一个函数,是一个增强的createStore,是使用applyMiddleware来生成的。

createStore的内部环境及返回值 :

createStore方法形成了一个闭包其中包含了几个重要的数据:

reducer、currentState(简称state)、currentListeners、nextListeners(简称listeners,其元素简称listener)复制代码

返回的是一个对象,这个对象中包含四个方法:

dispatch、subscribe、getState、replaceReducer复制代码

下面我们分别对这四个方法进行分析

dispatch:redux中唯一改变state树的方法,它可以分发一个action然后通过reducer改变state树,同时需要一个reducer执行结束才能进行下一次reducer,最后执行一次listener,因此每次state发生变化都会被监听到。

subscribe:用于订阅监听,为listeners增减就是通过它,最后返回销毁该listener的方法。

getState:获取当前state树。

replaceReducer: 替换当前store中的reducer,并初始化。

createStore通过闭包在内部创建了state状态树实现持久存储,然后通过暴露相应的方法实现对state的增删改查 compose.js

export default function compose(...funcs) {// 传入的参数是一个数组,数组长度为0时返回一个 arg => arg的函数  if (funcs.length === 0) {    return arg => arg  }// 当数组为一时返回这个数组的第一个元素  if (funcs.length === 1) {    return funcs[0]  }// 当数组为大于1的数时,返回一个从左至有依次嵌套的的函数,内层的函数的返回值作为外层函数的参数  return funcs.reduce((a, b) => (...args) => a(b(...args)))}复制代码

reduce方法在平时也会有使用,在平时经常使用reduce对数组求和,比如:

[1,2,3,4].reduce((a,b)=>a+b)复制代码

在这里的用法:

[funOne,funTwo,funThree].reduce((a, b) => (...args) => a(b(...args)))//相当于 function(...args) { return funOne(funTwo(funThree(...args)))}复制代码

从左至右依次嵌套,这样就可以理解compose的作用了

applyMiddleware.js

export default function applyMiddleware(...middlewares) {// 返回一个函数并将createStore作为参数然后返回一个新的函数,新的函数再将之前的reducer和preloadedState作为参数传入  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.`      )    }    // 建立新的变量middlewareAPI    const middlewareAPI = {      // getStates属性      getState: store.getState,      // dispatch属性      dispatch: (...args) => dispatch(...args)    }    // 传入middlewareAPI并执行每一个中间件,返回一个数组    const chain = middlewares.map(middleware => middleware(middlewareAPI))    // 重写dispatch    dispatch = compose(...chain)(store.dispatch)    return {      ...store,      dispatch    }  }}复制代码

applyMiddleware就是用于创建createStore中enhancer函数的。

bindActionCreator.js

// bindActionCreator接受两个参数actionCreator,dispatch,// 将action与dispatch结合并传入function bindActionCreator(actionCreator, dispatch) {  return function() {    return dispatch(actionCreator.apply(this, arguments))  }}export default function bindActionCreators(actionCreators, dispatch) {// 如果action是以函数形式传进来的,那么就调用bindActionCreator方法  if (typeof actionCreators === 'function') {    return bindActionCreator(actionCreators, dispatch)  }// actionCreators必须为对象类型  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"?`    )  } // 将actionCreators所有的属性名存入keys中  const keys = Object.keys(actionCreators)  const boundActionCreators = {}  //  给actionCreators的每一个成员都绑定dispatch方法生成新的方法,  // 然后注入新的对象中,新方法对应的key即为原来在actionCreators的名字  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}复制代码

bindActionCreators的作用是将action与dispatch绑定,这里面又分为两种情况,如果 bindActionCreators的第一个参数是函数的,则直接绑定。如果第一个参数是对象则将其对象中的每一个action方法绑定。

combineReducers.js

import ActionTypes from './utils/actionTypes'import warning from './utils/warning'import isPlainObject from './utils/isPlainObject'// 通过key和action生成错误信息function getUndefinedStateErrorMessage(key, action) {  const actionType = action && action.type  const actionDescription =    (actionType && `action "${
String(actionType)}"`) || 'an action' return ( `Given ${actionDescription}, reducer "${key}" returned undefined. ` + `To ignore an action, you must explicitly return the previous state. ` + `If you want this reducer to hold no value, you can return null instead of undefined.` )}//一些警告信息function getUnexpectedStateShapeWarningMessage( inputState, reducers, action, unexpectedKeyCache) { const reducerKeys = Object.keys(reducers) const argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState argument passed to createStore' : 'previous state received by the reducer' if (reducerKeys.length === 0) { return ( 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.' ) } if (!isPlainObject(inputState)) { return ( `The ${argumentName} has unexpected type of "` + {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + `". Expected argument to be an object with the following ` + `keys: "${reducerKeys.join('", "')}"` ) } const unexpectedKeys = Object.keys(inputState).filter( key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key] ) unexpectedKeys.forEach(key => { unexpectedKeyCache[key] = true }) if (action && action.type === ActionTypes.REPLACE) return if (unexpectedKeys.length > 0) { return ( `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` + `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` + `Expected to find one of the known reducer keys instead: ` + `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.` ) }}// 检测reducer是否符合redux的规定function assertReducerShape(reducers) { Object.keys(reducers).forEach(key => { const reducer = reducers[key] const initialState = reducer(undefined, { type: ActionTypes.INIT }) if (typeof initialState === 'undefined') { throw new Error( `Reducer "${key}" returned undefined during initialization. ` + `If the state passed to the reducer is undefined, you must ` + `explicitly return the initial state. The initial state may ` + `not be undefined. If you don't want to set a value for this reducer, ` + `you can use null instead of undefined.` ) } const type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random() .toString(36) .substring(7) .split('') .join('.') if (typeof reducer(undefined, { type }) === 'undefined') { throw new Error( `Reducer "${key}" returned undefined when probed with a random type. ` + `Don't try to handle ${ ActionTypes.INIT } or other actions in "redux/*" ` + `namespace. They are considered private. Instead, you must return the ` + `current state for any unknown actions, unless it is undefined, ` + `in which case you must return the initial state, regardless of the ` + `action type. The initial state may not be undefined, but can be null.` ) } })}/** * * reducer function. It will call every child reducer, and gather their results * into a single state object, whose keys correspond to the keys of the passed * reducer functions. * * @param {Object} reducers An object whose values correspond to different * reducer functions that need to be combined into one. One handy way to obtain * it is to use ES6 `import * as reducers` syntax. The reducers may never return * undefined for any action. Instead, they should return their initial state * if the state passed to them was undefined, and the current state for any * unrecognized action. * * @returns {Function} A reducer function that invokes every reducer inside the * passed object, and builds a state object with the same shape. */export default function combineReducers(reducers) { // 获取所有reducer的按键名 const reducerKeys = Object.keys(reducers) const finalReducers = {} // 过滤reducer for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (process.env.NODE_ENV !== 'production') { if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } // finalReducers保存最后过滤过后的reducer集合 // 存储过滤后的reducer的键名 const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } // 检测每个reducer是否是符合标准的reducer let shapeAssertionError try { assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } // return function combination(state = {}, action) { if (shapeAssertionError) { throw shapeAssertionError } // 如果不是生产环境的一些警告处理 if (process.env.NODE_ENV !== 'production') { const warningMessage = getUnexpectedStateShapeWarningMessage( state, finalReducers, action, unexpectedKeyCache ) if (warningMessage) { warning(warningMessage) } } let hasChanged = false // 下一个state树 const nextState = {} // 每发起一次dispatch都会遍历所有的reduce 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) // reducer返回的数据是undefined抛出错误 if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } // 将相应的值赋给nextState相应的属性 nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } //如果当前action对应的reducer方法执行完后,该处数据没有变化,则返回原来的流程树 return hasChanged ? nextState : state }}复制代码

combineReducers接受一个参数:

属性为reducer的对象复制代码

combineReducers方法从字面就能理解,它的作用是将所有的reduce合并,通过每一次发起dispatch改变状态。

转载地址:http://bhjhl.baihongyu.com/

你可能感兴趣的文章
Cocos数据篇[3.4](6) ——SQLite3数据库基础用法
查看>>
Linux应用总结(1):自动删除n天前日志
查看>>
跟我学习dubbo-简介(1)
查看>>
Rsync基本操作加实时同步演练
查看>>
第1章 选择流程控制语句
查看>>
Java之品优购课程讲义_day03(8)
查看>>
EasySchedule定时任务web平台
查看>>
这10道springboot常见面试题你需要了解下
查看>>
大数据学习知识点导图
查看>>
Android 开发环境搭建(MyEclipse+Android sdk+ADT环境)
查看>>
金蝶系统(V12),防火墙需要开启的端口
查看>>
一:响应头信息
查看>>
我的友情链接
查看>>
使用angularjs对数据进行排序筛选
查看>>
创建可弹性调整文件系统的 LVM 卷组
查看>>
mailx配置特定邮箱发信
查看>>
CentOS 6.5 Openstack Icehouse 安装指南 -6 计算 nova
查看>>
puppet
查看>>
saltstack自定义returner之利用fluent+mysql进行结果收集
查看>>
My Autoplay v12.0 多国语言最新版
查看>>