Redux源码学习笔记
前端之家收集整理的这篇文章主要介绍了
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;"> state
const dispatch </span>= action =><span style="color: #000000;"> {
state </span>=<span style="color: #000000;"> reducer(state,action)
listeners.forEach(listener </span>=><span style="color: #000000;"> listener())
}
const subscribe </span>= listener =><span style="color: #000000;"> {
listeners.push(listener)
</span><span style="color: #0000ff;">return</span> () =><span style="color: #000000;"> {
listeners </span>= listeners.filter(l => l !==<span style="color: #000000;"> listener)
}
}
dispatch({})
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> { 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;"> reducerskey
<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;">if</span> (<span style="color: #0000ff;">typeof</span> reducers[key] === 'function'<span style="color: #000000;">) {
finalReducers[key] </span>=<span style="color: #000000;"> 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;"> nextStateForKey
hasChanged </span>= hasChanged || nextStateForKey !==<span style="color: #000000;"> previousStateForKey
}
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 如果state每一个key都没有被修改 就直接返回原state</span>
<span style="color: #0000ff;">return</span> hasChanged ?<span style="color: #000000;"> 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 }
源码
import ActionTypes from './utils/actionTypes'
'./utils/warning''./utils/isPlainObject'
<span style="color: #0000ff;">function
<span style="color: #000000;"> getUndefinedStateErrorMessage(key,action) {
const actionType = action &&<span style="color: #000000;"> action.type
const actionDescription =<span style="color: #000000;">
(actionType &&
action "${String(actionType)}"
) || 'an action'
<span style="color: #0000ff;">return<span style="color: #000000;"> (
Given ${actionDescription},reducer </span>"${key}" returned undefined.
+<span style="color: #000000;">
To ignore an action,you must explicitly </span><span style="color: #0000ff;">return</span> the previous state.
+<span style="color: #000000;">
If you want </span><span style="color: #0000ff;">this</span> reducer to hold no value,you can <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">null</span><span style="color: #000000;"> instead of undefined.
)
}
<span style="color: #0000ff;">function<span style="color: #000000;"> getUnexpectedStateShapeWarningMessage(
inputState,reducers,action,unexpectedKeyCache
) {
const reducerKeys =<span style="color: #000000;"> Object.keys(reducers)
const argumentName =<span style="color: #000000;">
action && action.type ===<span style="color: #000000;"> ActionTypes.INIT
? 'preloadedState argument passed to createStore'<span style="color: #000000;">
: 'previous state received by the reducer'
<span style="color: #0000ff;">if (reducerKeys.length === 0<span style="color: #000000;">) {
<span style="color: #0000ff;">return<span style="color: #000000;"> (
'Store does not have a valid reducer. Make sure the argument passed ' +
'to combineReducers is an object whose values are reducers.'<span style="color: #000000;">
)
}
<span style="color: #0000ff;">if (!<span style="color: #000000;">isPlainObject(inputState)) {
<span style="color: #008000;">//<span style="color: #008000;"> 希望 inputState 是一个简单对象:通过 new Object() 、 {} 创建 (Object.create(null) 这里好像是不合法的
<span style="color: #008000;">//<span style="color: #008000;"> [object Array] 中提取 'Array'
<span style="color: #008000;">//<span style="color: #008000;"> Object.prototype.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1]
<span style="color: #0000ff;">return<span style="color: #000000;"> (
The ${argumentName} has unexpected type of </span>"<span style="color: #000000;">
+
{}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
</span>". Expected argument to be an object <span style="color: #0000ff;">with</span> the following
+<span style="color: #000000;">
keys: </span>"${reducerKeys.join('","')}"<span style="color: #000000;">
)
}
<span style="color: #008000;">//<span style="color: #008000;"> 检查所有Reducer都没有处理到的key ( 此处实在不解 unexpectedKeyCache 到底何用= =
const unexpectedKeys =<span style="color: #000000;"> Object.keys(inputState).filter(
key => !reducers.hasOwnProperty(key) && !<span style="color: #000000;">unexpectedKeyCache[key]
)
unexpectedKeys.forEach(key =><span style="color: #000000;"> {
unexpectedKeyCache[key] = <span style="color: #0000ff;">true<span style="color: #000000;">
})
<span style="color: #008000;">//<span style="color: #008000;"> 替换 store 的 Reducer 时会调用 dispatch({ type: ActionTypes.REPLACE })
<span style="color: #0000ff;">if (action && action.type === ActionTypes.REPLACE) <span style="color: #0000ff;">return
<span style="color: #0000ff;">if (unexpectedKeys.length > 0<span style="color: #000000;">) {
<span style="color: #0000ff;">return<span style="color: #000000;"> (
Unexpected ${unexpectedKeys.length </span>> 1 ? 'keys' : 'key'}
+<span style="color: #000000;">
</span>"${unexpectedKeys.join('","')}" found <span style="color: #0000ff;">in</span> ${argumentName}.
+<span style="color: #000000;">
Expected to find one of the known reducer keys instead:
+<span style="color: #000000;">
</span>"${reducerKeys.join('","')}"<span style="color: #000000;">. Unexpected keys will be ignored.
)
}
}
<span style="color: #0000ff;">function<span style="color: #000000;"> assertReducerShape(reducers) {
Object.keys(reducers).forEach(key =><span style="color: #000000;"> {
const reducer =<span style="color: #000000;"> reducers[key]
const initialState =<span style="color: #000000;"> reducer(undefined,{ type: ActionTypes.INIT })
<span style="color: #008000;">//<span style="color: #008000;"> Reducer"$ {key}"在初始化时返回undefined。如果传递给reducer的状态未定义,你必须明确返回初始状态。
<span style="color: #008000;">//<span style="color: #008000;"> 初始状态可以是不可定义。如果你不想为这个reducer设置一个值,你可以使用null而不是undefined。
<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof initialState === 'undefined'<span style="color: #000000;">) {
<span style="color: #0000ff;">throw <span style="color: #0000ff;">new<span style="color: #000000;"> Error(
Reducer </span>"${key}" returned undefined during initialization.
+<span style="color: #000000;">
If the state passed to the reducer is undefined,you must
+<span style="color: #000000;">
explicitly </span><span style="color: #0000ff;">return</span> the initial state. The initial state may
+<span style="color: #000000;">
not be undefined. If you don</span>'<span style="color: #000000;">t want to set a value for this reducer,
+
you can use null instead of undefined.
)
}
if (
typeof reducer(undefined,{
type: ActionTypes.PROBE_UNKNOWN_ACTION()
}) === </span>'undefined'<span style="color: #000000;">
) {
// 当使用随机类型探测Reducer${key}时返回undefined。
// 不要试图处理${ActionTypes.INIT}或者其他在"redux/*"命名空间的动作。它们被认为是私有的。
// 相反,当你遇到任何未知动作时,你必须返回当前的state,除非当前state是undefined,
// 那样你要返回初始状态,而不管动作类型。初始状态不可以是undefined,但可以为null
throw new Error(
`Reducer "${key}" returned undefined when probed with a random type. ` +
`Don</span>'t <span style="color: #0000ff;">try</span><span style="color: #000000;"> to handle ${
ActionTypes.INIT
} or other actions </span><span style="color: #0000ff;">in</span> "redux/*" ` +<span style="color: #000000;">
`namespace. They are considered private. Instead,you must </span><span style="color: #0000ff;">return</span> the ` +<span style="color: #000000;">
`current state </span><span style="color: #0000ff;">for</span> any unknown actions,unless it is undefined,` +<span style="color: #000000;">
`</span><span style="color: #0000ff;">in</span> which <span style="color: #0000ff;">case</span> you must <span style="color: #0000ff;">return</span> the initial state,regardless of the ` +<span style="color: #000000;">
`action type. The initial state may not be undefined,but can be </span><span style="color: #0000ff;">null</span><span style="color: #000000;">.`
)
}
})
}
<span style="color: #008000;">/<span style="color: #008000;">
-
Turns an object whose values are different reducer functions,into a single
-
reducer function. It will call every child reducer,and gather their results
-
into a single state object,whose keys correspond to the keys of the passed
-
reducer functions.
-
-
@param {Object} reducers An object whose values correspond to different
-
reducer functions that need to be combined into one. One handy way to obtain
-
it is to use ES6 import * as reducers
syntax. The reducers may never return
-
undefined for any action. Instead,they should return their initial state
-
if the state passed to them was undefined,and the current state for any
-
unrecognized action.
-
-
@returns {Function} A reducer function that invokes every reducer inside the
-
passed object,and builds a state object with the same shape.
<span style="color: #008000;">*/<span style="color: #000000;">
export <span style="color: #0000ff;">default <span style="color: #0000ff;">function<span style="color: #000000;"> combineReducers(reducers) {
const reducerKeys =<span style="color: #000000;"> Object.keys(reducers)
const finalReducers =<span style="color: #000000;"> {}
<span style="color: #0000ff;">for (let i = 0; i < reducerKeys.length; i++<span style="color: #000000;">) {
const key =<span style="color: #000000;"> reducerKeys[i]
<span style="color: #0000ff;">if (process.env.NODE_ENV !== 'production'<span style="color: #000000;">) {
<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof reducers[key] === 'undefined'<span style="color: #000000;">) {
warning(No reducer provided </span><span style="color: #0000ff;">for</span> key "${key}"<span style="color: #000000;">
)
}
}
<span style="color: #0000ff;">if (<span style="color: #0000ff;">typeof reducers[key] === 'function'<span style="color: #000000;">) {
finalReducers[key] =<span style="color: #000000;"> reducers[key]
}
}
const finalReducerKeys =<span style="color: #000000;"> Object.keys(finalReducers)
let unexpectedKeyCache
<span style="color: #0000ff;">if (process.env.NODE_ENV !== 'production'<span style="color: #000000;">) {
unexpectedKeyCache =<span style="color: #000000;"> {}
}
let shapeAssertionError
<span style="color: #0000ff;">try<span style="color: #000000;"> {
<span style="color: #008000;">//<span style="color: #008000;"> 判断每个reducer都有初始值和对于未知action返回原state
<span style="color: #000000;"> assertReducerShape(finalReducers)
} <span style="color: #0000ff;">catch<span style="color: #000000;"> (e) {
shapeAssertionError =<span style="color: #000000;"> e
}
<span style="color: #0000ff;">return <span style="color: #0000ff;">function combination(state =<span style="color: #000000;"> {},action) {
<span style="color: #0000ff;">if<span style="color: #000000;"> (shapeAssertionError) {
<span style="color: #0000ff;">throw<span style="color: #000000;"> shapeAssertionError
}
</span><span style="color: #0000ff;">if</span> (process.env.NODE_ENV !== 'production'<span style="color: #000000;">) {
const warningMessage </span>=<span style="color: #000000;"> getUnexpectedStateShapeWarningMessage(
state,finalReducers,unexpectedKeyCache
)
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (warningMessage) {
warning(warningMessage)
}
}
let hasChanged </span>= <span style="color: #0000ff;">false</span><span style="color: #000000;">
const nextState </span>=<span style="color: #000000;"> {}
</span><span style="color: #0000ff;">for</span> (let i = 0; i < finalReducerKeys.length; i++<span style="color: #000000;">) {
const key </span>=<span style="color: #000000;"> finalReducerKeys[i]
const reducer </span>=<span style="color: #000000;"> finalReducers[key]
const previousStateForKey </span>=<span style="color: #000000;"> state[key]
const nextStateForKey </span>=<span style="color: #000000;"> reducer(previousStateForKey,action)
</span><span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">typeof</span> nextStateForKey === 'undefined'<span style="color: #000000;">) {
const errorMessage </span>=<span style="color: #000000;"> getUndefinedStateErrorMessage(key,action)
</span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> Error(errorMessage)
}
nextState[key] </span>=<span style="color: #000000;"> nextStateForKey
hasChanged </span>= hasChanged || nextStateForKey !==<span style="color: #000000;"> previousStateForKey
}
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 如果state每一个key都没有被修改 就直接返回原state</span>
<span style="color: #0000ff;">return</span> hasChanged ?<span style="color: #000000;"> nextState : state
}
}
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;">return</span><span style="color: #000000;"> 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;">if</span><span style="color: #000000;"> (isDispatching) {
</span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> 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;">
)
}
let isSubscribed </span>= <span style="color: #0000ff;">true</span>
<span style="color: #008000;">//</span><span style="color: #008000;"> 每次修改 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;">//</span><span style="color: #008000;"> 返回一个当前监听者取消订阅的方法</span>
<span style="color: #0000ff;">return</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> unsubscribe() {
</span><span style="color: #0000ff;">if</span> (!<span style="color: #000000;">isSubscribed) {
</span><span style="color: #0000ff;">return</span><span style="color: #000000;">
}
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 正在派发 action 时不能进行操作</span>
<span style="color: #0000ff;">if</span><span style="color: #000000;"> (isDispatching) {
</span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> 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;">
)
}
isSubscribed </span>= <span style="color: #0000ff;">false</span><span style="color: #000000;">
ensureCanMutateNextListeners()
const index </span>=<span style="color: #000000;"> nextListeners.indexOf(listener)
nextListeners.splice(index,</span>1<span style="color: #000000;">)
}
}
<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;">if</span> (<span style="color: #0000ff;">typeof</span> action.type === 'undefined'<span style="color: #000000;">) {
</span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> Error(
</span>'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'<span style="color: #000000;">
)
}
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (isDispatching) {
</span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> Error('Reducers may not dispatch actions.'<span style="color: #000000;">)
}
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 用 isDispatching 记录是否正在 派发action 过程中不能进行其他操作</span>
isDispatching = <span style="color: #0000ff;">true</span><span style="color: #000000;">
currentState </span>=<span style="color: #000000;"> currentReducer(currentState,action)
} </span><span style="color: #0000ff;">finally</span><span style="color: #000000;"> {
isDispatching </span>= <span style="color: #0000ff;">false</span><span style="color: #000000;">
}
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 用到 listeners 才会修改 currentListeners 以减少修改次数</span>
const listeners = (currentListeners =<span style="color: #000000;"> nextListeners)
</span><span style="color: #0000ff;">for</span> (let i = 0; i < listeners.length; i++<span style="color: #000000;">) {
const listener </span>=<span style="color: #000000;"> listeners[i]
listener()
}
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> 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;"> 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;">function</span><span style="color: #000000;"> observeState() {
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (observer.next) {
observer.next(getState())
}
}
observeState()
const unsubscribe </span>=<span style="color: #000000;"> outerSubscribe(observeState)
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> { unsubscribe }
},[$$observable]() {
</span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">
}
}
}
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(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
dispatch
call so they
- may be invoked directly. This is just a convenience method,as you can call
store.dispatch(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
dispatch
function available on your Redux
- store.
-
- @returns {Function|Object} The object mimicking the original object,but with
- every action creator wrapped into the
dispatch
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;">function</span><span style="color: #000000;">,instead received ${ actionCreators </span>=== <span style="color: #0000ff;">null</span> ? 'null' : <span style="color: #0000ff;">typeof</span><span style="color: #000000;"> actionCreators }.
+<span style="color: #000000;">
Did you write </span>"import ActionCreators from" instead of "import * as ActionCreators from"?<span style="color: #000000;">
)
}
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的中间件嘛
以下参考 源码中
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 == 'dispatching'='next state'
呐,可能不止一个需求,现在我又想记录错误信息了。我们写两个方法,分别给dispatch添加自己想要的功能。
== '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的部分由 统一处理。
=IoUsly:
'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就可以了。
'dispatching'='next state'
这里的next就是之前的中间件处理后的dispatch,我们不再获取store的私有属性了,改为用参数传递。然后在处理之后(logger(store)(next))返回一个新的dispatch。
为什么这里要套两个函数而不是传入两个参数(store,next)呢,就相当于把这个函数柯里化了嘛……后面可以看到用处。
改成ES6的箭头函数
const logger = store => next => action =>'dispatching'
说实话虽然简洁了,但是看起来一点都不直观……可能是我太菜了。嗯,这就是一个中间件的写法了。
可以简单的实现下 applyMiddleware
===>=
这样就可以最后使用 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 redux-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 dispatch
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(
Dispatching </span><span style="color: #0000ff;">while</span> constructing your middleware is not allowed.
+<span style="color: #000000;">
Other middleware would not be applied to </span><span style="color: #0000ff;">this</span><span style="color: #000000;"> dispatch.
)
}
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。
勉强看完源码。假装自己理解了这样子。