资讯专栏INFORMATION COLUMN

redux源码分析之一:createStore.js

NotFound / 3424人阅读

摘要:定义了几个函数用于修改上面的几个局部变量主要包括函数用于获取用于替换用于修改列表用于触发执行,生成新的,并且,执行列表中的每一个函数完整解析请参考我的,如果对您有帮助,欢迎,有任何问题也请指正。

欢迎关注redux源码分析系列文章:
redux源码分析之一:createStore.js
redux源码分析之二:combineReducers.js
redux源码分析之三:bindActionCreators.js
redux源码分析之四:compose.js
redux源码分析之五:applyMiddleware

createStore.js是redux的核心文件,暴露了一个函数createStore,函数执行后返回一个对象,该对象包含了4个关键的方法:dispatch, subscribe, getState, replaceReducer,代码如下。

export default function createStore(reducer, preloadedState, enhancer) {
    //中间代码略
    return {
        dispatch,
        subscribe,
        getState,
        replaceReducer,
        [$$observable]: observable
      }
}
一、createStore函数的参数:

reducer:reducer是一个函数,该函数会返回一个全新的state,而state则保存了所有的数据

preloadedState:初始state

enhancer:这个参数特别有意思,如果该enhancer参数存在的话,会将当前的createStore函数作为参数传入enhancer函数,并且,enhancer执行之后得到一个新函数,该新函数其实就是一个加强版的createStore函数,新的函数会把之前的reducer和preloadeState作为参数传入并执行。这个enhancer参数为redux中间件提供了入口。

二、参数检查代码及异常处理:
//如果preloadedState没有传,但是enhancer参数传了,重置一下变量
if (typeof preloadedState === "function" && typeof enhancer === "undefined")          
  {
    enhancer = preloadedState
    preloadedState = undefined
  }
//如果enhancer传了,但是不是函数,则报错提示,否则执行enhancer函数,
//并继续执行enhancer函数返回的加强版的createStore函数,
//参数reducer以及preloadeState和原createStore函数保持一致
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函数,该reducer函数可以被动态替换掉
let currentState = preloadedState //保存了当前的state数据
let currentListeners = [] //保存了当前注册的函数列表
let nextListeners = currentListeners
let isDispatching = false  //是否正在dispatch一个action

最关键的是currentState变量,调用createStore之后,currentState变量保存了当前状态的所有数据

四、定义了几个函数:
//确保nextListeners和currentListeners不是同一个引用
function ensureCanMutateNextListeners() {
  if (nextListeners === currentListeners) {
    //如果是同一个引用,则浅拷贝currentListeners到nextListeners
    nextListeners = currentListeners.slice()
  }
}
//getState函数,返回局部变量currentState,以获取当前状态
function getState() {
  return currentState
}
  //注册一个函数,将注册函数放入局部变量nextListeners数组里面
  //注册函数的返回值是一个注销函数,注销函数执行可以将刚刚添加进nextListeners的listener函数又删除掉。这里很有意思,外部必须在调用subscribe执行现场保存好unsubscribe函数,否则将无法注销一个函数
  function subscribe(listener) {
    //如果listener不是函数,直接报错
    if (typeof listener !== "function") {
      throw new Error("Expected listener to be a function.")
    }

    let isSubscribed = true
    //确保nextListeners不是currentListeners,以保证修改的是nextListeners,而不是currentListeners
    ensureCanMutateNextListeners()
    //将监听函数放入监听函数列表尾部
    nextListeners.push(listener)

    //返回一个函数,该函数可以从监听函数列表中删除刚刚注册的监听函数
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }
  //触发action的函数:每次触发一个action,currentListeners中的所有函数都要执行一遍
  function dispatch(action) {
    //如果action不是普通的对象,直接报错
    if (!isPlainObject(action)) {
      throw new Error(
        "Actions must be plain objects. " +
        "Use custom middleware for async actions."
      )
    }
    //如果action没有type属性,直接报错:说明action对象必须要包含type字段
    if (typeof action.type === "undefined") {
      throw new Error(
        "Actions may not have an undefined "type" property. " +
        "Have you misspelled a constant?"
      )
    }
    //如果当前正在触发另外一个action,直接报错
    if (isDispatching) {
      throw new Error("Reducers may not dispatch actions.")
    }

    try {
      //先将标志位置为true
      isDispatching = true
      //执行传入的reducer函数,该函数返回一个新的state对象,并赋值给currentState变量
      currentState = currentReducer(currentState, action)
    } finally {
      //reducer函数执行完成后,将isDispatching恢复成false,方便下次action的触发
      isDispatching = false
    }

    //每一次触发一个action,所有的监听函数都要全部重新执行一遍,
    //并且把上次得到的新的监听函数列表赋值成为当前的监听函数列表。这是一个懒操作,并不是在subscribe的时候就操作了,而是在dispatch的时候才操作
    const listeners = currentListeners = nextListeners
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    //该dispatch函数的返回值是原来的action
    return action
  }
  //替换reducer函数:这个函数允许运行时动态替换最开始调用createStore函数时传入的reducer,并且替换掉reducer之后,重新dispatch一个action,得到全新的currentState对象
  function replaceReducer(nextReducer) {
    //如果nextReducer不是函数,直接报错
    if (typeof nextReducer !== "function") {
      throw new Error("Expected the nextReducer to be a function.")
    }
    //把新的reducer赋值给当前的currentReducer变量,得到一个全新的currentReducer
    currentReducer = nextReducer
    // 触发一个初始action:
    // 1.这样就可以完成一次监听函数列表的全部调用
    // 2.可以得到一个全新的currentState;
    dispatch({type: ActionTypes.INIT})
  }
function observable() {
    const outerSubscribe = subscribe
    return {
      /**
       * The minimal observable subscription method.
       * @param {Object} observer Any object that can be used as an observer.
       * The observer object should have a `next` method.
       * @returns {subscription} An object with an `unsubscribe` method that can
       * be used to unsubscribe the observable from the store, and prevent further
       * emission of values from the observable.
       */
      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,就会执行所有的注册函数,并且执行reducer函数,生成初始化的state

//马上内部调用一次初始化的操作,根据传入的reducer函数,preloadedState生成一个全新的currentState和全新的reducer
  dispatch({type: ActionTypes.INIT})

总结一下就是:

createStore函数定义了几个局部变量用于记录状态,主要包括currentState记录数据状态,currentListeners记录注册函数列表,currentReducer记录当前的reducer函数。

定义了几个函数用于修改上面的几个局部变量:主要包括getState函数用于获取currentState;replaceReducer用于替换currentReducer;subscribe用于修改currentListeners列表;dispatch用于触发currentReducer执行,生成新的currentState,并且,执行currentListeners列表中的每一个函数;

完整解析请参考我的github:https://github.com/abczhijia/...,如果对您有帮助,欢迎star,有任何问题也请指正。

(完)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/88897.html

相关文章

  • Redux 莞式教程 之 进阶篇

    摘要:进阶教程原文保持更新写在前面相信您已经看过简明教程,本教程是简明教程的实战化版本,伴随源码分析用的是编写,看到有疑惑的地方的,可以复制粘贴到这里在线编译总览在的源码目录,我们可以看到如下文件结构打酱油的,负责在控制台显示警告信息入口文件除去 Redux 进阶教程 原文(保持更新):https://github.com/kenberkele... 写在前面 相信您已经看过 Redux ...

    岳光 评论0 收藏0
  • 解密Redux: 从源码开始

    摘要:接下来笔者就从源码中探寻是如何实现的。其实很简单,可以简单理解为一个约束了特定规则并且包括了一些特殊概念的的发布订阅器。新旧中存在的任何都将收到先前的状态。这有效地使用来自旧状态树的任何相关数据填充新状态树。 Redux是当今比较流行的状态管理库,它不依赖于任何的框架,并且配合着react-redux的使用,Redux在很多公司的React项目中起到了举足轻重的作用。接下来笔者就从源码...

    remcarpediem 评论0 收藏0
  • 史上最全的 Redux 源码分析

    摘要:订阅器不应该关注所有的变化,在订阅器被调用之前,往往由于嵌套的导致发生多次的改变,我们应该保证所有的监听都注册在之前。 前言 用 React + Redux 已经一段时间了,记得刚开始用Redux 的时候感觉非常绕,总搞不起里面的关系,如果大家用一段时间Redux又看了它的源码话,对你的理解会有很大的帮助。看完后,在回来看Redux,有一种 柳暗花明又一村 的感觉 ,. 源码 我分析的...

    fai1017 评论0 收藏0
  • 我的源码阅读之路:redux源码剖析

    摘要:到月底了,小明的爸爸的单位发了工资总计块大洋,拿到工资之后第一件的事情就是上交,毫无疑问的,除非小明爸爸不要命了。当小明的爸爸收到这个通知之后,心的一块大石头也就放下来了。下面我们正式开始我们的源码阅读之旅。 前言 用过react的小伙伴对redux其实并不陌生,基本大多数的React应用用到它。一般大家用redux的时候基本都不会单独去使用它,而是配合react-redux一起去使用...

    CloudwiseAPM 评论0 收藏0
  • Redux 入坑进阶 - 源码解析

    摘要:否则的话,认为只是一个普通的,将通过也就是进一步分发。在本组件内的应用传递给子组件源码解析期待一个作为传入,里面是如果只是传入一个,则通过返回被绑定到的函数遍历并通过分发绑定至将其声明为的属性之一接收的作为传入。 原文链接:https://github.com/ecmadao/Co...转载请注明出处 本文不涉及redux的使用方法,因此可能更适合使用过redux的玩家翻阅? 预热...

    BothEyes1993 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<