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

storeredux中用来存储所有state树的一个对象,改变store内的状态的唯一方法时对它dispatch一个actionredux中通过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 = preloadedStatepreloadedState = 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 = reducerlet currentState = preloadedStatelet currentListeners = []let nextListeners = currentListenerslet 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 = trueensureCanMutateNextListeners()nextListeners.push(listener)return function unsubscribe() {//未注册 ,返回if (!isSubscribed) {return}isSubscribed = falseensureCanMutateNextListeners()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 ==> rootReducercurrentState = 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 = nextReducerdispatch({ type: ActionTypes.INIT })}function observable() {const outerSubscribe = subscribereturn {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的快照的值。

转载于:https://juejin.im/post/5a9d17ef6fb9a028df221ef9

Redux源码浅析系列(一):`CreateStore`相关推荐

  1. Redux源码浅析系列(二):`combineReducer`

    上一章,我们讲解了createStore.下面,我们来看一下combineReducer. 在redux中,我们禁止在应用中创建多个store(我们这里默认讨论的都是客户端应用,同构应用不适用这条规则 ...

  2. redux源码分析之一:createStore.js

    欢迎关注redux源码分析系列文章: redux源码分析之一:createStore.js redux源码分析之二:combineReducers.js redux源码分析之三:bindActionC ...

  3. Redux 源码解析系列(一) -- Redux的实现思想

    文章来源: IMweb前端社区 黄qiong(imweb.io) IMweb团队正在招聘啦,简历发至jayccchen@tencent.com Redux 其实是用来帮我们管理状态的一个框架,它暴露给 ...

  4. redux源码分析之二:combineReducers.js

    欢迎关注redux源码分析系列文章: redux源码分析之一:createStore.js redux源码分析之二:combineReducers.js redux源码分析之三:bindActionC ...

  5. 逐行阅读redux源码(二)combineReducers

    前情提要 逐行阅读redux源码(一)createStore 认识reducers 在我们开始学习源码之前,我们不妨先来看看何谓reducers: 如图所见,我们可以明白, reducer 是用来对初 ...

  6. 学习 redux 源码整体架构,深入理解 redux 及其中间件原理

    如果觉得内容不错,可以设为星标置顶我的公众号 1. 前言 你好,我是若川.这是学习源码整体架构系列第八篇.整体架构这词语好像有点大,姑且就算是源码整体结构吧,主要就是学习是代码整体结构,不深究其他不是 ...

  7. Redux源码分析(一)

    Redux源码分析(createStore) 使用redux都快3年了,到现在也没认真去了解一下源码罪过啊,所以需要对它进行一些分析和学习,一方面能更好的去使用它,另一方面也学习一下该框架的设计思路, ...

  8. Redux源码全篇浅读

    本文是关于 redux(3.7.2)源代码的一些浅读 在redux源码目录中 ,可以看到以下文件目录: |-- utils/|-- warning.js //打印error |-- 1. applyM ...

  9. 深入理解redux之从redux源码到react-redux的原理

    在使用react的过程中,用redux来管理应用中的状态,使应用流更清晰的同时也会有小小的疑惑,比如reducer在redux中时怎么发挥作用的,为什么只要写好reducer,和dispatch特定a ...

最新文章

  1. 序列内置方法详解(string/list/tuple)
  2. mysql批量创建数据库 导数据
  3. 在应用了皮肤的程序中制作透明的文本编辑控件(如:TcxMemo)
  4. 在access窗体中加图片_Access实战:一种不用按钮控件就能控制子窗体联动的方法...
  5. 全球及中国磁性分离滑轮行业竞争战略及未来产销需求预测报告2022版
  6. 【Linux系统编程】POSIX无名信号量
  7. C# 字符,字符串和文本处理。
  8. 24--反转字符串中的单词 III
  9. MATLAB把多行多列矩阵数据和文字写入txt文件
  10. Android圆角图片封装类--copy别人的,不能转载,我也就醉了,谢谢原创
  11. Openstack Object Store(Swift)设置公有存储的方法
  12. 组装电脑多少钱一台_客户花9000元组装一台电脑,奸商赚5000块,利润真吓人
  13. mysql之查询某段时间范围的数据
  14. 对比不同的JSON字符串或者对象转的字符串
  15. 移动安全-IOS逆向第三天——实战HOOK RSA/DES加密
  16. 蚂蚁小程序--自学笔记
  17. 常见的安全产品与服务整理
  18. jQuery插件的使用
  19. 对隐秘的角落电视剧的感悟以及认识
  20. JBoss学习和应用

热门文章

  1. 关于从基于Mult-Org的视图中查询数据的问题(转)
  2. 遇到一个由于事务控制不当造成错误的Bug
  3. NOIP2011聪明的质监员题解
  4. 安装php openssl扩展
  5. SSH访问控制,多次失败登录即封掉IP,防止暴力破解
  6. linux 11G R2 RAC 配置NTP服务器
  7. 设计模式中遵循的原则:单一职责、开发-封闭、依赖倒转
  8. 教你打造优秀IT博文
  9. SQLServer Date
  10. Dataguard - 通过主库热备方式创建容灾库