剖析 Redux 源码

也能够看我的博客 – 剖析 Redux 源码

剖析 Redux 源码

《剖析 Redux 源码》

TIP

Redux 是 JavaScript 状况容器,供应可展望化的状况治理。

作为 React 百口桶的一分子,Redux 可谓说也是名声响响,在 2016 年进修 JavaScript 想必没有多少人没听过吧。

这里,本文不是来教人人怎样运用 Redux 的 API 的,这一类的文章已许多,关于 Redux 的引见和进修能够点击以下链接:

Redux 体小精干(只要2kB)且没有任何依靠,因而本文想经由过程浏览 Redux 的源码来进修 Redux 的运用以及头脑。

源码构造

Redux 的源码构造很简单,我们能够直接看 src 目录下的代码:

.src
├── utils                #东西函数
├── applyMiddleware.js
├── bindActionCreators.js        
├── combineReducers.js     
├── compose.js       
├── createStore.js  
└── index.js             #进口 js

index.js

这个是悉数代码的进口:

import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'

function isCrushed() {}

if (
  process.env.NODE_ENV !== 'production' &&
  typeof isCrushed.name === 'string' &&
  isCrushed.name !== 'isCrushed'
) {
  warning(
    '。。。'
  )
}

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose
}

这里的 isCrushed 函数重要是为了考证在非生产环境下 Redux 是不是被紧缩(由于假如被紧缩了那末 (isCrushed.name !== 'isCrushed') 就是 true),假如被紧缩会给开辟者一个 warn 提醒)。

然后就是暴露 createStore combineReducers bindActionCreators applyMiddleware compose 这几个接口给开辟者运用,我们来一一剖析这几个 API。

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

    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 ...

  dispatch({ type: ActionTypes.INIT })

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

起首定义了一个 ActionTypes 对象,它是一个 action,是一个 Redux 的私有 action,不允许外界触发,用来初始化 Store 的状况树和转变 reducers 后初始化 Store 的状况树。

createStore

然后偏重来看 createStore 函数:

接收

它能够接收三个参数,reducer、preloadedState、enhancer:

  • reducer:是一个函数,返回下一个状况,接收两个参数:当前状况 和 触发的 action;

  • preloadedState:初始状况对象,能够很随便指定,比方服务端衬着的初始状况,然则假如运用 combineReducers 来天生 reducer,那必需坚持状况对象的 key 和 combineReducers 中的 key 相对应;

  • enhancer:store 的加强器函数,能够指定为 第三方的中心件,时刻游览,耐久化 等等,然则这个函数只能用 Redux 供应的 applyMiddleware 函数来天生;

依据传入参数的个数和范例,推断 reducer 、 preloadedState 、 enhancer;

返回

挪用完函数它返回的接口是 dispatch subscribe getState replaceReducer[$$observable]

这也是我们开辟中重要运用的几个接口。

enhancer

假如 enhancer 参数传入而且是个正当的函数,那末就是挪用 enhancer 函数(传入 createStore 来给它操纵),enhancer 函数返回的也是一个函数,在这里传入 reducerpreloadedState,而且返回函数挪用效果,停止当前函数实行;
在 enhancer 函数内里是怎样操纵运用的能够看 applyMiddleware 部份;

getState
function getState() {
  return currentState
}

这个函数能够猎取当前的状况,createStore 中的 currentState 贮存当前的状况树,这是一个闭包,这个参数会耐久存在,而且一切的操纵状况都是转变这个援用,getState 函数返回当前的 currentState

subscribe
function subscribe(listener) {
  // 推断传入的参数是不是为函数
  if (typeof listener !== 'function') {
    throw new Error('Expected listener to be a function.')
  }

  var isSubscribed = true

  ensureCanMutateNextListeners()
  nextListeners.push(listener)

  return function unsubscribe() {
    if (!isSubscribed) {
      return
    }

    isSubscribed = false

    ensureCanMutateNextListeners()
    var index = nextListeners.indexOf(listener)
    nextListeners.splice(index, 1)
  }
}

这个函数能够给 store 的状况增加定阅监听函数,一旦挪用 dispatch ,一切的监听函数就会实行;
nextListeners 就是贮存当前监听函数的列表,挪用 subscribe,传入一个函数作为参数,那末就会给 nextListeners 列表 push 这个函数;
同时挪用 subscribe 函数会返回一个 unsubscribe 函数,用来解绑当前传入的函数,同时在 subscribe 函数定义了一个 isSubscribed 标志变量来推断当前的定阅是不是已被解绑,解绑的操纵就是从 nextListeners 列表中删除当前的监听函数。

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

这个函数是用来触发状况转变的,他接收一个 action 对象作为参数,然后 reducer 依据 action 的属性 以及 当前 store 的状况来天生一个新的状况,给予当前状况,转变 store 的状况;
currentState = currentReducer(currentState, action)
这里的 currentReducer 是一个函数,他接收两个参数:当前状况 和 action,然后返回计算出来的新的状况;
然后遍历 nextListeners 列表,挪用每一个监听函数;

replaceReducer
function replaceReducer(nextReducer) {
  // 推断参数是不是是函数范例
  if (typeof nextReducer !== 'function') {
    throw new Error('Expected the nextReducer to be a function.')
  }

  currentReducer = nextReducer
  dispatch({ type: ActionTypes.INIT })
}

这个函数能够替代 store 当前的 reducer 函数,起首直接把 currentReducer = nextReducer,直接替代;
然后 dispatch({ type: ActionTypes.INIT }) ,用来初始化替代后 reducer 天生的初始化状况而且给予 store 的状况;

observable
function observable() {
  var outerSubscribe = subscribe
  return {
    subscribe(observer) {
      if (typeof observer !== 'object') {
        throw new TypeError('Expected the observer to be an object.')
      }

      function observeState() {
        if (observer.next) {
          observer.next(getState())
        }
      }

      observeState()
      var unsubscribe = outerSubscribe(observeState)
      return { unsubscribe }
    },

    [$$observable]() {
      return this
    }
  }
}

关于这个函数,是不直接暴露给开辟者的,它供应了给其他观察者形式/相应式库的交互操纵,详细可看 https://github.com/zenparsing/es-observable

last

末了实行 dispatch({ type: ActionTypes.INIT }),用来依据 reducer 初始化 store 的状况。

compose.js

compose 能够接收一组函数参数,从右到左来组合多个函数,然后返回一个组合函数;

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  const last = funcs[funcs.length - 1]
  const rest = funcs.slice(0, -1)
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}

applyMiddleware.js

export default function applyMiddleware(...middlewares) {
  // 这个返回的函数就是 enhancer,接收 createStore 函数,再返回一个函数,接收的实在只要 reducer 和 preloadedState;
  return (createStore) => (reducer, preloadedState, enhancer) => {
    var store = createStore(reducer, preloadedState, enhancer)
    var dispatch = store.dispatch
    var chain = []

    // 暴漏 getState 和 dispatch 给 第三方中心价运用
    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    // 制造第三方中心件运用 middlewareAPI 后返回的函数构成的数组
    chain = middlewares.map(middleware => middleware(middlewareAPI))

    // 连系这一组函数 和 dispatch 构成的新的 dispatch,然后这个暴漏给用户运用,而原有的 store.dispatch 是稳定的,然则不暴漏
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

applyMiddleware 函数的作用是组合 多个 中心件等等,然后返回一个函数(enhancer

还记得在 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 === applyMiddleware(...)

然后再实行 enhancer(createStore) 继承以后的操纵;

这里 enhancer(createStore) 等同于 (reducer, preloadedState, enhancer) => { ... }

然后再实行 enhancer(createStore)(reducer, preloadedState)

再回到 applyMiddleware ,这里挪用了 var store = createStore(reducer, preloadedState, enhancer)
如上所见,这里实行的时刻已没有 enhancer 参数了,因而会再次实行 createStore 函数的悉数部份,然后获得一个返回的实例 store

以后会天生一个新的 dispatch ,先保留下来原生的 dispatchvar dispatch = store.dispatch

var middlewareAPI = {
  getState: store.getState,
  dispatch: (action) => dispatch(action)
}

这一步是把 store 的 getStatedispatch 接口暴露给中心件来操纵: chain = middlewares.map(middleware => middleware(middlewareAPI))

末了组合 悉数中心件的返回值(函数)chainstore.dispatch,然后返回新的 dispatchdispatch = compose(...chain)(store.dispatch)

这里的 dispatch 并非原有的 store 的,而是经由组合中心件以后新的 dispatch

末了返回暴露给用户的接口:

return {
  ...store,
  dispatch
}

重要照样 store 原有的接口,然则用新的 dispatch 替代了原有的;这个函数实在就是依据中心件和store的接口天生新的 dispatch 然后暴露给用户。

combineReducers.js

这个函数能够组合一组 reducers(对象) ,然后返回一个新的 reducer 函数给 createStore 运用。

它接收一组 reducers 构成的对象,对象的 key 是对应 value(reducer函数)的状况称号;

比方: { userInfo: getUserInfo, list: getList }

userInfo 是依据 getUserInfo 函数计算出来的;

那末 store 内里的 state 构造就会是: { userInfo: ..., list: ... }

export default function combineReducers(reducers) {

  // 依据 reducers 天生终究正当的 finalReducers:value 为 函数
  var reducerKeys = Object.keys(reducers)
  var finalReducers = {}
  for (var i = 0; i < reducerKeys.length; i++) {
    var 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]
    }
  }

  var finalReducerKeys = Object.keys(finalReducers)

  if (process.env.NODE_ENV !== 'production') {
    var unexpectedKeyCache = {}
  }

  // 考证 reducer 是不是正当
  var sanityError
  try {
    assertReducerSanity(finalReducers)
  } catch (e) {
    sanityError = e
  }

  // 返回终究天生的 reducer
  return function combination(state = {}, action) {
    if (sanityError) {
      throw sanityError
    }

    if (process.env.NODE_ENV !== 'production') {
      var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)
      if (warningMessage) {
        warning(warningMessage)
      }
    }

    var hasChanged = false
    var nextState = {}
    for (var i = 0; i < finalReducerKeys.length; i++) {
      var key = finalReducerKeys[i]
      var reducer = finalReducers[key]
      var previousStateForKey = state[key]
      var nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === 'undefined') {
        var errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    // 遍历一遍看是不是转变,然后返回原有状况值或许新的状况值
    return hasChanged ? nextState : state
  }
}

终究返回一个 combination 也就是真正传入 createStore 的 reducer 函数;

这是一个规范的 reducer 函数,接收一个初始化状况 和 一个 action 参数;

每次挪用的时刻会去遍历 finalReducer (有用的 reducer 列表),猎取列表中每一个 reducer 对应的先前状况: var previousStateForKey = state[key]
看到这里就应该邃晓传入的 reducers 组合为何 key 要和 store 内里的 state 的 key 相对应;

然后获得当前遍历项的下一个状况: var nextStateForKey = reducer(previousStateForKey, action)
然后把它增加到团体的下一个状况: nextState[key] = nextStateForKey

每次遍历会推断团体状况是不是转变: hasChanged = hasChanged || nextStateForKey !== previousStateForKey

在末了,假如没有转变就返回原有状况,假如转变了就返回新天生的状况对象: return hasChanged ? nextState : state

bindActionCreators.js

bindActionCreators 函数能够天生直接触发 action 的函数;

实质上它只说做了这么一个操纵 bindActionFoo = (...args) => dispatch(actionCreator(...args))

因而我们直接挪用 bindActionFoo 函数就能够转变状况了;

接收两个参数,一个是 actionCreators( actionCreator 构成的对象,key 关于天生的函数名/或许是一个 actionCreator ),一个是 dispatch, store 实例中猎取;

function bindActionCreator(actionCreator, dispatch) {
  return (...args) => dispatch(actionCreator(...args))
}

export default function bindActionCreators(actionCreators, dispatch) {
  // 是一个函数,直接返回一个 bindActionCreator 函数,这个函数挪用 dispatch 触发 action
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  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"?`
    )
  }

  // 遍历对象,然后对每一个遍历项的 actionCreator 天生函数,将函数根据本来的 key 值放到一个对象中,末了返回这个对象
  var keys = Object.keys(actionCreators)
  var boundActionCreators = {}
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i]
    var actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

总结

浏览了一遍 Redux 的源码,实在是太精巧了,少依靠,少耦合,纯函数式。

    原文作者:阿城
    原文地址: https://segmentfault.com/a/1190000007092130
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞