Redux源码浅析系列(一):`CreateStore`

使用react+redux开发有一段时间了,刚开始使用并没有深入了解其源码,最近静下心来,阅读了一下,感触颇深。
本系列主要从createStore,combineReducer,compose,applyMiddleware几个方面进行讲解。本系列不会详细讲解redux的用法,因此可能更适合有redux使用经验的小伙伴们阅读。

storeredux中用来存储所有state树的一个对象,改变store内的状态的唯一方法时对它dispatch一个action
redux中通过createStore创建一个store,用来存放应用中所有状态树,一个应用中应该有且只有一个store
首先,我们先来回顾一下createStore的用法:
用法
createStore(reducer, [preloadedState], enhancer)

源码标注解读

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


export const ActionTypes = {
  INIT: '@@redux/INIT'
}

export default function createStore(reducer, preloadedState, enhancer) {
  //preloadedState是可选的
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }
  //通过`enhancer`重新构建一个增强过的`store`。
  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.')
  }

  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false
  /** 
   * 订阅器在每次`dispatch`调用之前都会保存一份快照。当你正在调用`listener`的时候,`subscribe`或者`unSubscribe`,
   * 对当前的`dispatch`都不会有影响。
   * 但是,对于下一次`dispatch`,不管是否嵌套,都会使用`subscribe`列表中最近的一次快照。
  */
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice()
    }
  }


  function getState() {
    return currentState
  }
  //注册一个`change listener`,每当`dispatch`一个`action`的时候就会触发这个`listener`。
  //同时,返回一个`unsubscribe`函数,用来取消注册监听函数。
  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)
      //从`listener list`中剔除当前`listener`。
      nextListeners.splice(index, 1)
    }
  }
  //分发`action`,这是改变`store`中`state`的唯一方式。
  function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
        'Use custom middleware for async actions.'
      )
    }

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

    try {
      isDispatching = true
      //使用`getState`的结果和传入的`action`以同步的方式调用`store`的`reduce`函数,返回值会被作为下一个`state`.
      //currentReducer ==> rootReducer
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }
    //确保即使在 `listener`中进行`subscribe`或者`unSubscribe`,对当前的`dispatch`也不会有任何的影响。
    const listeners = currentListeners = nextListeners
    //遍历调用各个`listener`
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }

  //替换 `store` 当前用来计算 `state` 的 `reducer`
  function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
      throw new Error('Expected the nextReducer to be a function.')
    }

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


  function observable() {
    const 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()
        const unsubscribe = outerSubscribe(observeState)
        return { unsubscribe }
      },

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


  dispatch({ type: ActionTypes.INIT })

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


针对,subscribedispatch这两个方法,有几点需要注意的地方:

1.虽然在注册的listener里面调用dispatch在技术上可行的,但是由于每一次dispatch一个action的时候都会造成所有listener的执行,而listener在执行的时候又会进行dispatch的调用,这样可能会陷入一个无穷的循环当中。
2.关于函数ensureCanMutateNextListeners的作用。我在刚开始看redux的时候,曾一度很纠结ensureCanMutateNextListeners这个函数的作用,感觉明明可以直接用currentListeners的,为什么非得多此一举把它进行一份拷贝呢?
后来,在阅读了reduxapi之后,发现了这样一句话。
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.
大概翻译一下,就是说:
订阅器(subscriptions) 在每次 dispatch() 调用之前都会保存一份快照。当你在正在调用监听器(listener)的时候订阅(subscribe)或者去掉订阅(unsubscribe),对当前的 dispatch() 不会有任何影响。但是对于下一次的 dispatch(),无论嵌套与否,都会使用订阅列表里最近的一次快照。
可能还有点迷茫,那我们来举个例子说明一下:
下面是目前redux dispatch的源码:

function subscribe(listener){
    ...
    ensureCanMutateNextListeners()
    nextListeners.push(listener)
    ...
}
function dispatch(action){
    ...
    const listeners = currentListeners = nextListeners
    //确保即使在 listener中进行subscribe或者unSubscribe,对当前的dispatch也不会有任何的影响。
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
    ...
}

假如我们不使用nextListeners这个快照的话,那么代码会变成下面这个样子:

function subscribe(listener){
    ...
    currentListeners.push(listener)
    ...
}
function dispatch(action){
    ...
    for (let i = 0; i < currentListeners.length; i++) {
      const listener = currentListeners[i]
      listener()
    }
    ...
}

注意到什么了吗?还没有?好,那我们继续往下看:
假如,我们设置了这样一个listener

function listenerA(){
    store.subscribe(listenerA);
}

那么,当我们dispatch一个action之后,会发生什么?

首先,它会触发所有的listener,假设我们当前就只有这么一个。那么currentListeners.length===1,然后在执行这个listener的时候,它又会绑定一个新的listener。这时候currentListeners.length===2,for循环会继续执行下一个listener,然后currentListeners.length===3…这样,就会陷入一个死循环。

当我们使用了nextListeners这个快照之后,情况又如何呢?

同样,当我们dispatch一个action之后,它会触发所有的listener,然后在执行这个listener的时候,又会绑定一个新的listener。好,到此一次循环结束。这时候来看一下listeners.length的值,因为有了nextListeners这个快照的存在,listeners.length的值还是1。不管我们在listener中做什么样的操作,subscribe还是unsubscribe,对当前的dispatch都是没有影响的。
而下一次的listeners又是最近一次listener list的快照的值。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值