Redux源码学习笔记
时间:2019-02-05 分类:常见问题 作者:编程之家
https://github.com/redux js/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.dis patch(MyActionCreators.doSomething()) 换成一个 转成一个 函数
我们使用 action 时 是先通过 actionCreator创建action 然后通过 dis patch 派发出去
通过 bindActionCreator(actionCreator,dis patch) 获得一个 可以直接创建action并派发的函数
bindActionCreators 就是创建一个 对象 每个属性 都是一个 可以直接创建action并派发的函数
例:
action.increase = (info) => { type:'INCREASE'
= (info) => { type:'DECREASE'bindActionCreators({
increase: action.increase,decrease: action.decrease
},dis patch)
<span style="color: #008000;">// <span style="color: #008000;"> 就可以获得:
<span style="color: #000000;">{
increase: (...args) =><span style="color: #000000;">
dis patch(action.increase(...args)),decrease: (...args) =><span style="color: #000000;">
dis patch(action.decrease(...args))
}
源码:
dispatch) {
dis patch(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 firs t argument,* and get a function in return.
@para m {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.
@para m {Function} dis patch 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,
dis patch) {
<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,
dis patch)
}
<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,dis patch)
}
}
<span style="color: #0000ff;">return<span style="color: #000000;"> boundActionCreators
}
这个应该是最难理解的部分 所以放到最后看>_<
个人理解,这个东西就是在dis patch前后做一些事情=.= 类似koa express的中间件嘛
以下参考 源码中 edux/docs/advanced/Middleware.md
middleware 在dis patch和action之间提供一个 第三方程序扩展点。
现在一步一步理解applyMiddleware在做什么
首先,假设现在有一个 需求,每次dis patch一个 action时,都要打印action和state,像下面这样:
const action = addT
odo ('Use R
edux '
console.log( '
dis patching'<span style="color: #000000;">,action)
store.
dis patch(action)
console.log('next state',store.getState())
但是不可能每一次都这样打印,也许直接修改 dis patch就可以
const next =
dispatch
store.dis patch =
dispatchAndLog(action) {
console.log( 'dis patching'='next state'
呐,可能不止一个 需求,现在我又想记录错误 信息了。我们写两个方法 ,分别给dis patch添加 自己想要的功能 。
etoAddLogging(store) {
const next =dispatch
store.dis patch = dispatchAndLog(action) {
console.log( 'dis patching'='next state' <span style="color: #0000ff;">function <span style="color: #000000;"> patchStoreto AddCrashReporting(store) {
const next =<span style="color: #000000;"> store.dis patch
store.dis patch = <span style="color: #0000ff;">function <span style="color: #000000;"> dis patchAndReportErrors(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
}
}
}
patchStoreto AddLogging(store)
patchStoreto AddCrashReporting(store)
但是这样并不好……很明显,我们在修改 store的私有属性 了,emm m……这是一个 比较hack的方法 ……要改的优雅一点,把修改 dis patch的部分封装起来。每一次返回新的dis patch,修改 store的部分由 统一处理。
=dispatch
IoUsly:
dispatch = function dis patchAndLog(action) {
dispatchAndLog(action) {
console.log( 'dis patching' <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 dis patch function with each middleware.
middlewares.forEach(middleware =><span style="color: #000000;">
store.dis patch =<span style="color: #000000;"> middleware(store)
)
}
applyMiddlewareByMonkeypatching(store,[logger,crashReporter])
但是这样还是不太好。dis patch是store的私有属性 ,我们却直接获取 了。思考我们为什么重写dis patch,因为我们在用多个中间件的时候,第一个 中间件修改 完dis patch,下一次修改 应该是在前一个 的基础之上,包裹上一次修改 的dis patch。但其实,这也不是必要的,只要每一次传入上一次修改 后的dis patch就可以了。
dispatchToAddLogging(next) {
dispatchAndLog(action) {
console.log( 'dis patching'='next state'
这里的next就是之前的中间件处理后的dis patch,我们不再获取 store的私有属性 了,改为用参数传递。然后在处理之后(logger(store)(next))返回一个 新的dis patch。
为什么这里要套两个函数 而不是传入两个参数(store,next)呢,就相当于把这个函数柯里化 了嘛……后面可以看到用处。
改成ES6的箭头函数
const logger = store => next => action =>
'dis patching'
说实话虽然简洁了,但是看起来一点都不直观……可能是我太菜了。嗯,这就是一个 中间件的写法了。
可以简单的实现下 applyMiddleware
=dispatch =dispatch
middlewares.forEach(middleware =>dispatch =dispatch)
)
dispatch })
}
这样就可以最后使用 applyMiddleware
import { createStore,combineReducers,applyMiddleware } from 'r
edux '
const todo App =<span style="color: #000000;"> combineReducers(reducers)
const store =<span style="color: #000000;"> createStore(
t
odo App,<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 dis patch 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 firs t
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.
@para m {...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 dis patch = () =><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;">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;"> <a href="https://www.jb51.cc/tag/dis/" target="_blank" class="keywords">dis</a>patch.
)
}
const middlewareAPI =<span style="color: #000000;"> {
getState: store.getState,dis patch: (...args) =><span style="color: #000000;"> dis patch(...args)
}
const chain = middlewares.map(middleware =><span style="color: #000000;"> middleware(middlewareAPI))
dis patch =<span style="color: #000000;"> compose(...chain)(store.dis patch)
<span style="color: #0000ff;">return<span style="color: #000000;"> {
...store,dis patch
}
}
}
applyMiddleware([middlewares]) 就是返回一个 函数 传入createStore,返回新的createStore,创建的store的dis patch是经过中间件加工的。
这里可以看到编写中间件嵌套两个函数 的用处,先传入一个 store,只需要再传入一个 最新的dis patch就可以了,就是把dis patch用中间件轮流处理一下。这里使用了compose。
勉强看完源码。假装自己理解了这样子。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。