Redux源码分析之基本概念

Redux源码分析之createStore

Redux源码分析之bindActionCreators

Redux源码分析之combineReducers

Redux源码分析之compose

Redux源码分析之applyMiddleware

接着前面的,我们继续,打开createStore.js, 直接看最后, createStore返回的就是一个带着5个方法的对象。

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

同样的,我先删除一些不需要的代码,简化成如下, 注意看备注。(注:这里先无视中间件和enhancer,后篇再说)

export const ActionTypes = {
INIT: '@@redux/INIT'
}
export default function createStore(reducer, preloadedState, enhancer) {
// 初始化参数
let currentReducer = reducer // 整个reducer
let currentState = preloadedState //当前的state, getState返回的值就是他,
let currentListeners = [] // 当前的订阅,搭配 nextListeners
let nextListeners = currentListeners //下一次的订阅 ,搭配currentListeners
let isDispatching = false //是否处于 dispatch action 状态中 // 内部方法
function ensureCanMutateNextListeners() { } // 确保currentListeners 和 nextListeners 是不同的引用
function getState() { } // 获得当前的状态,返回的就是currentState
function subscribe(listener) { } //订阅监听,返回一个函数,执行该函数,取消监听
function dispatch(action) { } // dispacth action
function replaceReducer(nextReducer) { } // 替换 reducer
function observable() { } //不知道哈哈 //初始化state
dispatch({ type: ActionTypes.INIT }) //返回方法
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}

 

ensureCanMutateNextListeners

这个方法主要用在 subscribe里面,

  • 在每次订阅和取消订阅的时候,会让 nextListeners 和 currentListeners 不是同一个引用,
  • 在每次 dispatch的时候,当 reducer执行完毕,订阅执行前,让 nextListeners 和 currentListeners 是一个引用
  function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}

  为什么这么设计,在subscribe方法上有很详细的注解,我的理解是假如订阅在执行过程中,这里说的是订阅执行过程,不是reducer执行过程

有新加的订阅添加的时候,新的订阅是不会被执行的,因为是一份拷贝

有新的订阅删除的时候,被删除的还是会执行的。

       简单说,就是新的删除和添加,下次生效。

 
getState
就是返回利用闭包存的currentState
  /**
* Reads the state tree managed by the store.
*
* @returns {any} The current state tree of your application.
*/
function getState() {
return currentState
}

 

 subscribe

添加订阅

  • 每次添加前,如果 nextListeners 和 currentListeners 是一个引用,重新复制一个
  • 并存入 nextListeners
  • 返回一个函数,执行该函数取消订阅,
  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)
nextListeners.splice(index, 1) // 从nextListeners里面删除,下次dispatch会生效
}
}

dispatch

派发一个action,让reducer更新数据,下面都有注释了,为啥可说的。
  •  如果上一次派发没完毕,接着派发是会出异常的,对于单线程的js来说倒是没啥大问题
  function dispatch(action) {
if (!isPlainObject(action)) { // action 必须是对象
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
} if (typeof action.type === 'undefined') { // 必须有type属性
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 // 标记,正在派发
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false //标记派发完毕
} const listeners = currentListeners = nextListeners // 让nextListeners生效
for (let i = 0; i < listeners.length; i++) { // 挨个执行订阅
const listener = listeners[i]
listener()
} return action // 返回action
}

  

replaceReducer
 function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') { // 不是函数,抛出异常
throw new Error('Expected the nextReducer to be a function.')
} currentReducer = nextReducer // 替换reducer
dispatch({ type: ActionTypes.INIT }) // 重新初始化
}

  

observable 还没啥研究,暂不说了。
最后的代码为, 
  • 初始化 state
  • 返回相关方法
  dispatch({ type: ActionTypes.INIT })

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

  这里说一下 dispatch({ type: ActionTypes.INIT }) 是怎么达到初始化state的,

  我们再回头看一下disptach中的一段代码

    try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}

      这里先讲非合成的reducer,带合成的后面说。

createStore的第一个参数为 reducer,第二个为初始化state的默认值,

  • 如果你传入了第二个参数,currentState就等于你传入的值,而执行一次 dispatch的时候,系统定义的 type 为@@redux/INIT的action,你肯定是没有定义的额,看下面代码,就会直接返回state,   那么执行 currentReducer(currentState, action) 得到的结果还是 currentState
  • 如果你没有传入第二个参数,在reducer的第一个参数指定了默认值,那么reducer处理type为 @@redux/INIT的action的时候,返回的就是reducer第一个参数 state的默认值,然后被赋值给了currentState
  • 如果没有传入第二个参数,同时reducer的state也没指定值,那么,你的dispatch一般都会报错,因为你的state从开始就是undefined
  • 如果recuder函数和createStore都设置了默认了,那么reducer的默认值是不会生效的
let todoReducer = function (state = todoList, action) {
switch (action.type) {
case 'add':
return [...state, action.todo]
case 'delete':
return state.filter(todo => todo.id !== action.id)
default:
return state
}
}

  这里相对特别的是 合成recuder,后面再说。

 

到此为止,你只用redux的createStore方法,就能完成数据控制了,combineReducers,bindActionCreators,applyMiddleware,compose 都只是对redux的增强。

再回头看看我们第一篇提到的代码:(云淡风轻)

  • 初始化的state在recuder赋值,和在createStore赋值是等价的,都赋值的话,createStore的赋值会生效。 (createStore用的是显示赋值, reducer:默认参数) 
/* 简单示例 */
let { createStore } = self.Redux //默认state
let todoList = []
// reducer
let todoReducer = function (state, action) {
switch (action.type) {
case 'add':
return [...state, action.todo]
case 'delete':
return state.filter(todo => todo.id !== action.id)
default:
return state
}
} //创建store
let store = createStore(todoReducer,todoList) //订阅
function subscribe1Fn() {
console.log(store.getState())
}
let sub = store.subscribe(subscribe1Fn) store.dispatch({
type: 'add',
todo: {
id: 1,
content: '学习redux'
}
}) store.dispatch({
type: 'add',
todo: {
id: 2,
content: '吃饭睡觉'
}
}) store.dispatch({
type: 'delete',
id: 2
}) // 取消订阅
sub() console.log('取消订阅后:')
store.dispatch({
type: 'add',
todo: {
id: 3,
content: '打游戏'
}
})

   

Redux源码分析之createStore的更多相关文章

  1. Redux源码分析之applyMiddleware

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  2. Redux源码分析之基本概念

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  3. Redux源码分析之bindActionCreators

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  4. Redux源码分析之combineReducers

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  5. Redux源码分析之compose

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  6. redux源码图解:createStore 和 applyMiddleware

    在研究 redux-saga时,发现自己对 redux middleware 不是太了解,因此,便决定先深入解读一下 redux 源码.跟大多数人一样,发现 redux源码 真的很精简,目录结构如下: ...

  7. redux源码学习笔记 - createStore

    本篇是学习redux源码的一些记录,学习的redux版本是^4.0.1. 在页面开发时,需要管理很多状态(state),比如服务器响应,缓存数据,UI状态等等···当页面的庞大时,状态就会变的混乱.r ...

  8. 史上最全的 Redux 源码分析

    前言 用 React + Redux 已经一段时间了,记得刚开始用Redux 的时候感觉非常绕,总搞不起里面的关系,如果大家用一段时间Redux又看了它的源码话,对你的理解会有很大的帮助.看完后,在回 ...

  9. 正式学习React(四) ----Redux源码分析

    今天看了下Redux的源码,竟然出奇的简单,好吧.简单翻译做下笔记: 喜欢的同学自己可以去github上看:点这里 createStore.js import isPlainObject from ' ...

随机推荐

  1. 有关Dom的一些操作

    学习前端的都会了解到一些Dom操作,让我们来看看Dom操作有哪些吧! DOM(即 Document Object Mode) 是 W3C(万维网联盟)的标准. DOM 定义了访问 HTML 和 XML ...

  2. AutoMapper5.0创建对象方法更新

    /// <summary> /// 单个对象映射 /// </summary> public static TDestination MapTo<TSource, TDe ...

  3. Haoop MapReduce 的Partition和reduce端的二次排序

    先贴一张原理图(摘自hadoop权威指南第三版) 实际中看了半天还是不太理解其中的Partition,和reduce端的二次排序,最终根据实验来结果来验证自己的理解 1eg 数据如下 20140101 ...

  4. [leetcode-592-Fraction Addition and Subtraction]

    Given a string representing an expression of fraction addition and subtraction, you need to return t ...

  5. AngularJS数据双向绑定

    <body ng-app> <div ng-controller="myCtrl"> <input ng-model="abc" ...

  6. 微信支付(APP)

    折腾了一天,终于搞定了微信支付,总结一下.首先从服务器端获取prepareid,Andorid 端再根据这个prepareid二次签名. 服务器端: 从官网上下载DEMO,Demo中只有JsAPi,M ...

  7. Unity 游戏框架搭建 (五) 简易消息机制

    什么是消息机制? 23333333,让我先笑一会. 为什么用消息机制?   三个字,解!!!!耦!!!!合!!!!. 我的框架中的消息机制用例: 1.接收者 ``` using UnityEngine ...

  8. C#工作笔记

    没想到一个Java后端开发还要负责C#桌面程序,我感觉有点方.不过方归方,活还是要干的.简单记录下学到的一些知识点. 1.引用API函数 namespace Demo { class MyUtil { ...

  9. AngularJS实用基础知识---入门必备

    前言 今天来和大家学习一下AngularJS-- AngularJS 通过新的属性和表达式扩展了 HTML. AngularJS 可以构建一个单一页面应用程序. AngularJS 学习起来非常简单. ...

  10. Struts 框架 之 Hello World

    Struts HelloWorld 第一步   导jar包 commons-fileupload-1.2.2.jar   [文件上传相关包] commons-io-2.0.1.jar     [输入输 ...