Redux源码学习笔记

https://github.com/reduxjs/redux 版本 4.0.0

先了解一下redux是怎么用的,此处摘抄自阮一峰老师的《

<span style="color: #008000;">//<span style="color: #008000;"> store 是保存数据的地方

<span style="color: #008000;">//<span style="color: #008000;"> 创建 store
import { createStore } from 'redux'<span style="color: #000000;">
const store =<span style="color: #000000;"> createStore(fn)

<span style="color: #008000;">//<span style="color: #008000;"> state 是某一时刻 store 的快照,一个 state 对应一个 view<span style="color: #008000;">
//<span style="color: #008000;"> 可通过 getState() 获取
const state =<span style="color: #000000;"> store.getState()

<span style="color: #008000;">//<span style="color: #008000;"> Action 是一个对象 用来表示 view 发出的改变 state 的通知<span style="color: #008000;">
//<span style="color: #008000;"> type 是必须的 其他属性可以自由设置
const action =<span style="color: #000000;"> {
type: 'ADD_TODO'<span style="color: #000000;">,payload: 'Learn Redux'<span style="color: #000000;">
}

<span style="color: #008000;">//<span style="color: #008000;"> 同一种类型的 action 可以写一个函数生成
const ADD_TODO = '添加 TODO'
<span style="color: #008000;">//<span style="color: #008000;"> 生成 action 的函数: Action Creator
<span style="color: #0000ff;">function<span style="color: #000000;"> addTodo(text) {
<span style="color: #0000ff;">return<span style="color: #000000;"> {
type: ADD_TODO,text
}
}

const action = addTodo('Learn Redux'<span style="color: #000000;">)

<span style="color: #008000;">//<span style="color: #008000;"> store.dispatch()是 View 发出 Action 的唯一方法。
<span style="color: #000000;">store.dispatch(action)

<span style="color: #008000;">//<span style="color: #008000;"> reducer 是 store 接收 state 返回新的 state 的过程
<span style="color: #000000;">
const defaultState = 0
<span style="color: #008000;">//<span style="color: #008000;"> reducer 接收 action 返回新的 state
const reducer = (state = defaultState,action) =><span style="color: #000000;"> {
<span style="color: #0000ff;">switch<span style="color: #000000;">(action.type) {
<span style="color: #0000ff;">case: 'ADD'<span style="color: #000000;">:
<span style="color: #0000ff;">return state +<span style="color: #000000;"> action.payload
<span style="color: #0000ff;">default<span style="color: #000000;">:
<span style="color: #0000ff;">return<span style="color: #000000;"> state
}
}
const state = reducer(1<span style="color: #000000;">,{
type: 'ADD'<span style="color: #000000;">,payload: 2<span style="color: #000000;">
})

<span style="color: #008000;">//<span style="color: #008000;"> 创建 store 时传入 reducer 当调用 store.dispatch 时将自动调用 reducer
const store =<span style="color: #000000;"> createStore(reducer)

<span style="color: #008000;">/*<span style="color: #008000;">
reducer 是一个纯函数,纯函数要求:

  • 不得改写参数
  • 不能调用系统 I/O 的API
  • 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果
    <span style="color: #008000;">*/

<span style="color: #008000;">//<span style="color: #008000;"> store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数<span style="color: #008000;">
//<span style="color: #008000;"> 返回解除监听函数
let unsubscribe = store.subsribe(() =><span style="color: #000000;"> { console.log(store.getState) })
unsubscribe() <span style="color: #008000;">//<span style="color: #008000;"> 解除监听

<span style="color: #008000;">/*<span style="color: #008000;">
store 提供的三个方法

  • store.getState()
  • store.dispatch()
  • store.subscribe()
    <span style="color: #008000;">*/

<span style="color: #008000;">//<span style="color: #008000;"> createStore方法还可以接受第二个参数,表示 State 的最初状态。这通常是服务器给出的。<span style="color: #008000;">
//<span style="color: #008000;"> !这个初始值会覆盖 Reducer 函数默认的初始值
let store =<span style="color: #000000;"> createStore(todoApp,STATE_FROM_SERVER)

<span style="color: #008000;">//<span style="color: #008000;"> createStore 的简单实现
const createStore = (reducer) =><span style="color: #000000;"> {
let state
let listeners =<span style="color: #000000;"> []

const getState </span>= () =><span style="color: #000000;"&gt; state

const dispatch </span>= action =><span style="color: #000000;"&gt; {
    state </span>=<span style="color: #000000;"&gt; reducer(state,action)
    listeners.forEach(listener </span>=><span style="color: #000000;"&gt; listener())
}

const subscribe </span>= listener =><span style="color: #000000;"&gt; {
    listeners.push(listener)
    </span><span style="color: #0000ff;"&gt;return</span> () =><span style="color: #000000;"&gt; {
        listeners </span>= listeners.filter(l => l !==<span style="color: #000000;"&gt; listener)
    }
}

dispatch({})

</span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; { getState,dispatch,subscribe }

}

<span style="color: #008000;">//<span style="color: #008000;"> 可以通过 combineReducers 来将多个 Reducer 合为一个
import { combineReducers } from 'redux'<span style="color: #000000;">

const chatReducer =<span style="color: #000000;"> combineReducers({
chatLog,statusMessage,userName
})

<span style="color: #008000;">//<span style="color: #008000;"> combineReducer 的简单实现
const combineReducers = reducers =><span style="color: #000000;"> {
<span style="color: #0000ff;">return (state = {},action) =><span style="color: #000000;">
Object.keys(reducers).reduce(
(nextState,key) =><span style="color: #000000;"> {
nextState[key] =<span style="color: #000000;"> reducers
key
<span style="color: #0000ff;">return<span style="color: #000000;"> nextState
},{}
)
}

工作流程

dispatch(action) (previousState,action)

Action Creators ======> Store ======><span style="color: #000000;"> Reducers
^ || <======<span style="color: #000000;">
_ ||<span style="color: #000000;"> (newState)
_ (state) ||<span style="color: #000000;">
_ ||<span style="color: #000000;">
(view opt)_ \/<span style="color: #000000;">
\--- React Comonents

OK 可以开始看源码了~ 网上Redux源码分析的博客真的非常多.. 不过当你知道他的源码究竟有多短 就能理解了hhh 

combineReducers.js 

代码一共179行 多是错误处理 我先将错误处理全部删掉 便只剩28行.....

思路就是创建一个对象 将 Reducer 全部放进去

当Action传进来的时候 就让每一个Reducer去处理这个action

每个Reducer都有一个对应的key 只处理state中对应字段 state[key] 没有Reducer对应的字段会被忽略

截取出核心代码 + 用法、感觉并不需要注释、逻辑都很直接

== (let i = 0; i < reducerKeys.length; i++=</span><span style="color: #0000ff;"&gt;if</span> (<span style="color: #0000ff;"&gt;typeof</span> reducers[key] === 'function'<span style="color: #000000;"&gt;) { finalReducers[key] </span>=<span style="color: #000000;"&gt; reducers[key] }

}
const finalReducerKeys =<span style="color: #000000;"> Object.keys(finalReducers)

<span style="color: #0000ff;">return <span style="color: #0000ff;">function combination(state =<span style="color: #000000;"> {},action) {
let hasChanged = <span style="color: #0000ff;">false<span style="color: #000000;">
const nextState =<span style="color: #000000;"> {}
<span style="color: #0000ff;">for (let i = 0; i < finalReducerKeys.length; i++<span style="color: #000000;">) {
const key =<span style="color: #000000;"> finalReducerKeys[i]
const reducer =<span style="color: #000000;"> finalReducers[key]
const previousStateForKey =<span style="color: #000000;"> state[key]
const nextStateForKey =<span style="color: #000000;"> reducer(previousStateForKey,action)

  nextState[key] </span>=<span style="color: #000000;"&gt; nextStateForKey
  hasChanged </span>= hasChanged || nextStateForKey !==<span style="color: #000000;"&gt; previousStateForKey
}
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 如果state每一个key都没有被修改 就直接返回原state</span>
<span style="color: #0000ff;"&gt;return</span> hasChanged ?<span style="color: #000000;"&gt; nextState : state

}
}

<span style="color: #008000;">/*<span style="color: #008000;">**** 下面是简单的用法实例 ****<span style="color: #008000;">*/
<span style="color: #0000ff;">function todos(state =<span style="color: #000000;"> [],action) {
<span style="color: #0000ff;">switch<span style="color: #000000;"> (action.type) {
<span style="color: #0000ff;">case 'ADD_TODO'<span style="color: #000000;">:
<span style="color: #0000ff;">return<span style="color: #000000;"> state.concat(action.text)
<span style="color: #0000ff;">default<span style="color: #000000;">:
<span style="color: #0000ff;">return<span style="color: #000000;"> state
}
}

<span style="color: #0000ff;">function counter(state = 0<span style="color: #000000;">,action) {
<span style="color: #0000ff;">switch<span style="color: #000000;"> (action.type) {
<span style="color: #0000ff;">case 'INCREMENT'<span style="color: #000000;">:
<span style="color: #0000ff;">return state + 1
<span style="color: #0000ff;">case 'DECREMENT'<span style="color: #000000;">:
<span style="color: #0000ff;">return state - 1
<span style="color: #0000ff;">default<span style="color: #000000;">:
<span style="color: #0000ff;">return<span style="color: #000000;"> state
}
}

let reducer =<span style="color: #000000;"> combineReducers({ list: todos,number: counter })
let state = { list: [],number: 0,otherKey: 'no reducer match will be ignore'<span style="color: #000000;"> }
console.log(state) <span style="color: #008000;">//<span style="color: #008000;"> { list: [],otherKey: 'no reducer match will be ignore' }
state = reducer(state,{ type: 'ADD_TODO',text: 'study'<span style="color: #000000;"> })
console.log(state) <span style="color: #008000;">//<span style="color: #008000;"> { list: [ 'study' ],number: 0 }
state = reducer(state,text: 'sleep'<span style="color: #000000;"> })
console.log(state) <span style="color: #008000;">//<span style="color: #008000;"> { list: [ 'study','sleep' ],{ type: 'INCREMENT'<span style="color: #000000;"> })
console.log(state) <span style="color: #008000;">//<span style="color: #008000;"> { list: [ 'study',number: 1 }

 源码

const randomString = () =>367'''.' const ActionTypes =/INIT${randomString()}`,REPLACE: `@@redux/REPLACE${randomString()}`,PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}` export <span style="color: #0000ff;">default ActionTypes

是redux核心代码,不过这个没有什么难理解的地方

import $$observable from 'symbol-observable'import ActionTypes from './utils/actionTypes'<span style="color: #000000;">
import isPlainObject from './utils/isPlainObject'

<span style="color: #008000;">//<span style="color: #008000;"> 创建 store 的函数<span style="color: #008000;">
//<span style="color: #008000;"> preloadedState: store设置的初始值 这个值会覆盖 Reducer 的默认值<span style="color: #008000;">
//<span style="color: #008000;"> 如果使用了 combineReducers preloadedState 要和 combineReducers 有相同的keys<span style="color: #008000;">
//<span style="color: #008000;"> enhancer: 中间件
export <span style="color: #0000ff;">default <span style="color: #0000ff;">function<span style="color: #000000;"> createStore(reducer,preloadedState,enhancer) {
<span style="color: #008000;">//<span style="color: #008000;"> preloadedState可以不传 判断preloadedState是否存在
<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof preloadedState === 'function' && <span style="color: #0000ff;">typeof enhancer === 'undefined'<span style="color: #000000;">) {
enhancer =<span style="color: #000000;"> preloadedState
preloadedState =<span style="color: #000000;"> undefined
}

<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof enhancer !== 'undefined'<span style="color: #000000;">) {
<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof enhancer !== 'function'<span style="color: #000000;">) {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new Error('Expected the enhancer to be a function.'<span style="color: #000000;">)
}
<span style="color: #008000;">//<span style="color: #008000;"> enhancer是一个高阶函数 调用enhancer返回一个"加强版"的createStore
<span style="color: #0000ff;">return<span style="color: #000000;"> enhancer(createStore)(reducer,preloadedState)
}

<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof reducer !== 'function'<span style="color: #000000;">) {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new Error('Expected the reducer to be a function.'<span style="color: #000000;">)
}

let currentReducer =<span style="color: #000000;"> reducer
let currentState =<span style="color: #000000;"> preloadedState
let currentListeners =<span style="color: #000000;"> []
let nextListeners =<span style="color: #000000;"> currentListeners
let isDispatching = <span style="color: #0000ff;">false
<span style="color: #008000;">//<span style="color: #008000;"> 判断当前 nextListeners 和 currentListeners 是否为同一个对象
<span style="color: #008000;">//<span style="color: #008000;"> 如果是一个对象 就把 nextListeners 改为 currentListeners 的副本
<span style="color: #0000ff;">function<span style="color: #000000;"> ensureCanMutateNextListeners() {
<span style="color: #0000ff;">if (nextListeners ===<span style="color: #000000;"> currentListeners) {
nextListeners =<span style="color: #000000;"> currentListeners.slice()
}
}
<span style="color: #008000;">//<span style="color: #008000;"> 获取当前对象 如果是正在派发action 则不能获取state
<span style="color: #0000ff;">function<span style="color: #000000;"> getState() {
<span style="color: #0000ff;">if<span style="color: #000000;"> (isDispatching) {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new<span style="color: #000000;"> Error(
'You may not call store.getState() while the reducer is executing. ' +
'The reducer has already received the state as an argument. ' +
'Pass it down from the top reducer instead of reading it from the store.'<span style="color: #000000;">
)
}

</span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; currentState

}
<span style="color: #008000;">//<span style="color: #008000;"> 订阅 添加订阅者
<span style="color: #0000ff;">function<span style="color: #000000;"> subscribe(listener) {
<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof listener !== 'function'<span style="color: #000000;">) {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new Error('Expected the listener to be a function.'<span style="color: #000000;">)
}

</span><span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; (isDispatching) {
  </span><span style="color: #0000ff;"&gt;throw</span> <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; Error(
    </span>'You may not call store.subscribe() while the reducer is executing. ' +
      'If you would like to be notified after the store has been updated,subscribe from a ' +
      'component and invoke store.getState() in the callback to access the latest state. ' +
      'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'<span style="color: #000000;"&gt;
  )
}

let isSubscribed </span>= <span style="color: #0000ff;"&gt;true</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 每次修改 nextListeners 都要判断一下 nextListeners 和 currentListeners 是否为同一个对象</span>

<span style="color: #000000;"> ensureCanMutateNextListeners()
<span style="color: #008000;">//<span style="color: #008000;"> 注意 这里修改 nextListeners 之后并没有改变 currentListeners 而是在下一次用到 currentListeners 才会改变
<span style="color: #000000;"> nextListeners.push(listener)

</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 返回一个当前监听者取消订阅的方法</span>
<span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;function</span><span style="color: #000000;"&gt; unsubscribe() {
  </span><span style="color: #0000ff;"&gt;if</span> (!<span style="color: #000000;"&gt;isSubscribed) {
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt;
  }
  </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 正在派发 action 时不能进行操作</span>
  <span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; (isDispatching) {
    </span><span style="color: #0000ff;"&gt;throw</span> <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; Error(
      </span>'You may not unsubscribe from a store listener while the reducer is executing. ' +
        'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'<span style="color: #000000;"&gt;
    )
  }

  isSubscribed </span>= <span style="color: #0000ff;"&gt;false</span><span style="color: #000000;"&gt;

  ensureCanMutateNextListeners()
  const index </span>=<span style="color: #000000;"&gt; nextListeners.indexOf(listener)
  nextListeners.splice(index,</span>1<span style="color: #000000;"&gt;)
}

}

<span style="color: #0000ff;">function<span style="color: #000000;"> dispatch(action) {
<span style="color: #0000ff;">if (!<span style="color: #000000;">isPlainObject(action)) {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new<span style="color: #000000;"> Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'<span style="color: #000000;">
)
}

</span><span style="color: #0000ff;"&gt;if</span> (<span style="color: #0000ff;"&gt;typeof</span> action.type === 'undefined'<span style="color: #000000;"&gt;) {
  </span><span style="color: #0000ff;"&gt;throw</span> <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; Error(
    </span>'Actions may not have an undefined "type" property. ' +
      'Have you misspelled a constant?'<span style="color: #000000;"&gt;
  )
}

</span><span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; (isDispatching) {
  </span><span style="color: #0000ff;"&gt;throw</span> <span style="color: #0000ff;"&gt;new</span> Error('Reducers may not dispatch actions.'<span style="color: #000000;"&gt;)
}

</span><span style="color: #0000ff;"&gt;try</span><span style="color: #000000;"&gt; {
  </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 用 isDispatching 记录是否正在 派发action 过程中不能进行其他操作</span>
  isDispatching = <span style="color: #0000ff;"&gt;true</span><span style="color: #000000;"&gt;
  currentState </span>=<span style="color: #000000;"&gt; currentReducer(currentState,action)
} </span><span style="color: #0000ff;"&gt;finally</span><span style="color: #000000;"&gt; {
  isDispatching </span>= <span style="color: #0000ff;"&gt;false</span><span style="color: #000000;"&gt;
}
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 用到 listeners 才会修改 currentListeners 以减少修改次数</span>
const listeners = (currentListeners =<span style="color: #000000;"&gt; nextListeners)
</span><span style="color: #0000ff;"&gt;for</span> (let i = 0; i < listeners.length; i++<span style="color: #000000;"&gt;) {
  const listener </span>=<span style="color: #000000;"&gt; listeners[i]
  listener()
}

</span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; action

}

<span style="color: #008000;">//<span style="color: #008000;"> 替换 Reducer 并派发动作 ActionTypes.REPLACE 相当于对state重新进行初始化
<span style="color: #0000ff;">function<span style="color: #000000;"> replaceReducer(nextReducer) {
<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof nextReducer !== 'function'<span style="color: #000000;">) {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new Error('Expected the nextReducer to be a function.'<span style="color: #000000;">)
}

currentReducer </span>=<span style="color: #000000;"&gt; nextReducer
dispatch({ type: ActionTypes.REPLACE })

}
<span style="color: #008000;">//<span style="color: #008000;"> emmmm...看不懂这个 可以参考 https://distums.github.io/2017/03/19/observables-proposal-for-ecmascript/
<span style="color: #0000ff;">function<span style="color: #000000;"> observable() {
const outerSubscribe =<span style="color: #000000;"> subscribe
<span style="color: #0000ff;">return<span style="color: #000000;"> {
subscribe(observer) {
<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof observer !== 'object' || observer === <span style="color: #0000ff;">null<span style="color: #000000;">) {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new TypeError('Expected the observer to be an object.'<span style="color: #000000;">)
}

    </span><span style="color: #0000ff;"&gt;function</span><span style="color: #000000;"&gt; observeState() {
      </span><span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; (observer.next) {
        observer.next(getState())
      }
    }

    observeState()
    const unsubscribe </span>=<span style="color: #000000;"&gt; outerSubscribe(observeState)
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; { unsubscribe }
  },[$$observable]() {
    </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;
  }
}

}

dispatch({ type: ActionTypes.INIT })

<span style="color: #0000ff;">return<span style="color: #000000;"> {
dispatch,subscribe,getState,replaceReducer,[$$observable]: observable
}
}

此处参考 《

按注释上说 这只是一个 convenience method

你可以把 store.dispatch(MyActionCreators.doSomething()) 换成一个转成一个函数

我们使用 action 时 是先通过 actionCreator创建action 然后通过 dispatch 派发出去

通过 bindActionCreator(actionCreator,dispatch) 获得一个可以直接创建action并派发的函数

bindActionCreators 就是创建一个对象 每个属性都是一个 可以直接创建action并派发的函数

例:

action.increase = (info) => { type:'INCREASE'= (info) => { type:'DECREASE'bindActionCreators({
increase: action.increase,decrease: action.decrease
},dispatch)

<span style="color: #008000;">//<span style="color: #008000;"> 就可以获得:
<span style="color: #000000;">{
increase: (...args) =><span style="color: #000000;"> dispatch(action.increase(...args)),decrease: (...args) =><span style="color: #000000;"> dispatch(action.decrease(...args))
}

源码:

dispatch) { dispatch(actionCreator.apply(<span style="color: #008000;">/<span style="color: #008000;">

  • Turns an object whose values are action creators,into an object with the
  • same keys,but with every function wrapped into a <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch call so they
  • may be invoked directly. This is just a convenience method,as you can call
  • store.<a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch(MyActionCreators.doSomething()) yourself just fine.
  • For convenience,you can also pass a single function as the first argument,* and get a function in return.
  • @param {Function|Object} actionCreators An object whose values are action
  • creator functions. One handy way to obtain it is to use ES6 import * as
  • Syntax. You may also pass a single function.
  • @param {Function} dispatch The <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch function available on your Redux
  • store.
  • @returns {Function|Object} The object mimicking the original object,but with
  • every action creator wrapped into the <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch call. If you passed a
  • function as actionCreators,the return value will also be a single
  • function.
    <span style="color: #008000;">*/<span style="color: #000000;">
    export <span style="color: #0000ff;">default <span style="color: #0000ff;">function<span style="color: #000000;"> bindActionCreators(actionCreators,dispatch) {
    <span style="color: #008000;">//<span style="color: #008000;"> 如果 actionCreators 是一个函数 说明只有一个 actionCreator
    <span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof actionCreators === 'function'<span style="color: #000000;">) {
    <span style="color: #0000ff;">return<span style="color: #000000;"> bindActionCreator(actionCreators,dispatch)
    }

<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof actionCreators !== 'object' || actionCreators === <span style="color: #0000ff;">null<span style="color: #000000;">) {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new<span style="color: #000000;"> Error(
bindActionCreators expected an object or a </span><span style="color: #0000ff;"&gt;function</span><span style="color: #000000;"&gt;,instead received ${ actionCreators </span>=== <span style="color: #0000ff;"&gt;null</span> ? 'null' : <span style="color: #0000ff;"&gt;typeof</span><span style="color: #000000;"&gt; actionCreators }. +<span style="color: #000000;">
Did you write </span>"import ActionCreators from" instead of "import * as ActionCreators from"?<span style="color: #000000;"&gt;
)
}

const keys =<span style="color: #000000;"> Object.keys(actionCreators)
const boundActionCreators =<span style="color: #000000;"> {}
<span style="color: #0000ff;">for (let i = 0; i < keys.length; i++<span style="color: #000000;">) {
const key =<span style="color: #000000;"> keys[i]
const actionCreator =<span style="color: #000000;"> actionCreators[key]
<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof actionCreator === 'function'<span style="color: #000000;">) {
boundActionCreators[key] =<span style="color: #000000;"> bindActionCreator(actionCreator,dispatch)
}
}
<span style="color: #0000ff;">return<span style="color: #000000;"> boundActionCreators
}

这个应该是最难理解的部分 所以放到最后看>_<

个人理解,这个东西就是在dispatch前后做一些事情=.= 类似koa express的中间件嘛

以下参考 源码中 edux/docs/advanced/Middleware.md

middleware 在dispatch和action之间提供一个第三方程序扩展点。

现在一步一步理解applyMiddleware在做什么

首先,假设现在有一个需求,每次dispatch一个action时,都要打印action和state,像下面这样:

const action = addTodo('Use Redux'console.log('dispatching'<span style="color: #000000;">,action)
store.dispatch(action)
console.log('next state',store.getState())

但是不可能每一次都这样打印,也许直接修改dispatch就可以

const next =dispatch store.dispatch = dispatchAndLog(action) { console.log('dispatching'='next state'

呐,可能不止一个需求,现在我又想记录错误信息了。我们写两个方法,分别给dispatch添加自己想要的功能

etoAddLogging(store) { const next =dispatch store.dispatch = dispatchAndLog(action) { console.log('dispatching'='next state'<span style="color: #0000ff;">function<span style="color: #000000;"> patchStoretoAddCrashReporting(store) {
const next
=<span style="color: #000000;"> store.dispatch
store.dispatch
= <span style="color: #0000ff;">function
<span style="color: #000000;"> dispatchAndReportErrors(action) {
<span style="color: #0000ff;">try
<span style="color: #000000;"> {
<span style="color: #0000ff;">return<span style="color: #000000;"> next(action)
} <span style="color: #0000ff;">catch<span style="color: #000000;"> (err) {
console.error('Caught an exception!'<span style="color: #000000;">,err)
Raven.captureException(err,{
extra: {
action,state: store.getState()
}
})
<span style="color: #0000ff;">throw<span style="color: #000000;"> err
}
}
}

patchStoretoAddLogging(store)
patchStoretoAddCrashReporting(store)

但是这样并不好……很明显,我们在修改store的私有属性了,emmm……这是一个比较hack的方法……要改的优雅一点,把修改dispatch的部分封装起来。每一次返回新的dispatch,修改store的部分由  统一处理。

=dispatch IoUsly: dispatch = function dispatchAndLog(action) { dispatchAndLog(action) { console.log('dispatching'<span style="color: #0000ff;">function<span style="color: #000000;"> applyMiddlewareByMonkeypatching(store,middlewares) {
middlewares
=<span style="color: #000000;"> middlewares.slice()
middlewares.reverse()
<span style="color: #008000;">//
<span style="color: #008000;"> Transform dispatch function with each middleware.

middlewares.forEach(middleware =><span style="color: #000000;">
store.dispatch
=<span style="color: #000000;"> middleware(store)
)
}

applyMiddlewareByMonkeypatching(store,[logger,crashReporter])

但是这样还是不太好。dispatch是store的私有属性,我们却直接获取了。思考我们为什么重写dispatch,因为我们在用多个中间件的时候,第一个中间件修改dispatch,下一次修改应该是在前一个的基础之上,包裹上一次修改dispatch。但其实,这也不是必要的,只要每一次传入上一次修改后的dispatch就可以了。

dispatchToAddLogging(next) { dispatchAndLog(action) { console.log('dispatching'='next state'

这里的next就是之前的中间件处理后的dispatch,我们不再获取store的私有属性了,改为用参数传递。然后在处理之后(logger(store)(next))返回一个新的dispatch。

为什么这里要套两个函数而不是传入两个参数(store,next)呢,就相当于把这个函数柯里化了嘛……后面可以看到用处。

改成ES6的箭头函数

const logger = store => next => action =>'dispatching'

说实话虽然简洁了,但是看起来一点都不直观……可能是我太菜了。嗯,这就是一个中间件的写法了。

可以简单的实现下 applyMiddleware

=dispatch =dispatch middlewares.forEach(middleware =>dispatch =dispatch) ) dispatch }) }

这样就可以最后使用 applyMiddleware

import { createStore,combineReducers,applyMiddleware } from 'redux'const todoApp =<span style="color: #000000;"> combineReducers(reducers)
const store =<span style="color: #000000;"> createStore(
todoApp,<span style="color: #008000;">//<span style="color: #008000;"> applyMiddleware() tells createStore() how to handle middleware
<span style="color: #000000;"> applyMiddleware(logger,crashReporter)
)

深入(meiyou)的理解之后 开始看applyMiddleware.js源码

其中用到里 compose 要先看一下

compose.js

这个是函数式编程的一个……思想?应用?

函数的嵌套调用写成组合   相当于   

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

if (funcs.length === 1) {
return funcs[0]
}
// reduce的参数..
// reduce(function(accumulator,currentValue,currentIndex,array) {...})
return funcs.reduce((a,b) => (...args) => a(b(...args)))
}

/** 使用示例 **/

let a = x => 'a' + x + 'a'
let b = x => 'b' + x + 'b'
let c = x => 'c' + x + 'c'
let foo = compose(b,a)
console.log(foo('v')) // bcavacb
let bar = x => b(c(a(x)))
console.log(bar('v')) // bcavacb

 最后看applyMiddleware.js

import compose from './compose'

<span style="color: #008000;">/<span style="color: #008000;">

  • Creates a store enhancer that applies middleware to the dispatch method

  • of the Redux store. This is handy for a variety of tasks,such as expressing

  • asynchronous actions in a concise manner,or logging every action payload.

  • See r<a href="https://www.jb51.cc/tag/edux/" target="_blank" class="keywords">edux</a>-thunk package as an example of the Redux middleware.

  • Because middleware is potentially asynchronous,this should be the first

  • store enhancer in the composition chain.

  • Note that each middleware will be given the <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch and getState functions

  • as named arguments.

  • @param {...Function} middlewares The middleware chain to be applied.

  • @returns {Function} A store enhancer applying the middleware.
    <span style="color: #008000;">*/<span style="color: #000000;">
    export <span style="color: #0000ff;">default <span style="color: #0000ff;">function<span style="color: #000000;"> applyMiddleware(...middlewares) {
    <span style="color: #0000ff;">return createStore => (...args) =><span style="color: #000000;"> {
    const store =<span style="color: #000000;"> createStore(...args)
    let dispatch = () =><span style="color: #000000;"> {
    <span style="color: #0000ff;">throw <span style="color: #0000ff;">new<span style="color: #000000;"> Error(
    <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patching </span><span style="color: #0000ff;"&gt;while</span> constructing your middleware is not allowed. +<span style="color: #000000;">
    Other middleware would not be applied to </span><span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt; <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch.
    )
    }

    const middlewareAPI =<span style="color: #000000;"> {
    getState: store.getState,dispatch: (...args) =><span style="color: #000000;"> dispatch(...args)
    }
    const chain = middlewares.map(middleware =><span style="color: #000000;"> middleware(middlewareAPI))
    dispatch =<span style="color: #000000;"> compose(...chain)(store.dispatch)

    <span style="color: #0000ff;">return<span style="color: #000000;"> {
    ...store,dispatch
    }
    }
    }

applyMiddleware([middlewares]) 就是返回一个函数 传入createStore,返回新的createStore,创建的store的dispatch是经过中间件加工的。

这里可以看到编写中间件嵌套两个函数的用处,先传入一个store,只需要再传入一个最新的dispatch就可以了,就是把dispatch用中间件轮流处理一下。这里使用了compose。

勉强看完源码。假装自己理解了这样子。

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...