大佬教程收集整理的这篇文章主要介绍了Redux源码学习笔记,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
https://github.com/reduxjs/redux 版本 4.0.0
<span style="color: #008000;">//
<span style="color: #008000;"> store 是保存数据的地方<span style="color: #008000;">//
<span style="color: #008000;"> 创建 store<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 是一个纯函数,纯函数要求:
<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 提供的三个方法
<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;"> ReducersOK 可以开始看源码了~ 网上Redux源码分析的博客真的非常多.. 不过当你知道他的源码究竟有多短 就能理解了hhh
combineReducers.js
代码一共179行 多是错误处理 我先将错误处理全部删掉 便只剩28行.....
思路就是创建一个对象 将 Reducer 全部放进去
当Action传进来的时候 就让每一个Reducer去处理这个action
每个Reducer都有一个对应的key 只处理state中对应字段 state[key] 没有Reducer对应的字段会被忽略
截取出核心代码 + 用法、感觉并不需要注释、逻辑都很直接
</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
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 }
s.js 源码
<span style="color: #0000ff;">function
<span style="color: #000000;"> getUndefinedStateErrormessage(key,action) {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
}
}
s.js
是redux核心代码,不过这个没有什么难理解的地方
<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
}
}
s.js
此处参考 《http://imweb.io/topic/5a426d32a192c3b460fce354" target="_blank">mapStateToProps,mapDispatchToProps的使用姿势》
按注释上说 这只是一个 convenience method
你可以把 store.dispatch(MyActionCreators.doSomething()) 换成一个转成一个函数
我们使用 action 时 是先通过 actionCreator创建action 然后通过 dispatch 派发出去
通过 bindActionCreator(actionCreator,dispatch) 获得一个可以直接创建action并派发的函数
bindActionCreators 就是创建一个对象 每个属性都是一个 可以直接创建action并派发的函数
例:
源码:
dispatch
call so theystore.dispatch(MyActionCreators.doSomething())
yourself just fine.import * as
dispatch
function available on your Reduxdispatch
call. If you passed aactionCreators
,the return value will also be a single<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
}
ymiddleware.js
这个应该是最难理解的部分 所以放到最后看>_<
个人理解,这个东西就是在dispatch前后做一些事情=.= 类似koa express的中间件嘛
以下参考 源码中
@H_272_0@middleware 在dispatch和action之间提供一个第三方程序扩展点。现在一步一步理解applymiddleware在做什么
首先,假设现在有一个需求,每次dispatch一个action时,都要打印action和state,像下面这样:
但是不可能每一次都这样打印,也许直接修改dispatch就可以
呐,可能不止一个需求,现在我又想记录错误信息了。我们写两个方法,分别给dispatch添加自己想要的功能。
patchStoretoaddLogging(storE)
patchStoretoaddCrashReporTing(storE)
但是这样并不好……很明显,我们在修改store的私有属性了,emmm……这是一个比较hack的方法……要改的优雅一点,把修改dispatch的部分封装起来。每一次返回新的dispatch,修改store的部分由 ymiddlewareBymonkeypatching 统一处理。
applymiddlewareBymonkeypatching(store,[logger,crashReporter])
但是这样还是不太好。dispatch是store的私有属性,我们却直接获取了。思考我们为什么重写dispatch,因为我们在用多个中间件的时候,第一个中间件修改完dispatch,下一次修改应该是在前一个的基础之上,包裹上一次修改的dispatch。但其实,这也不是必要的,只要每一次传入上一次修改后的dispatch就可以了。
这里的next就是之前的中间件处理后的dispatch,我们不再获取store的私有属性了,改为用参数传递。然后在处理之后(logger(storE)(next))返回一个新的dispatch。
为什么这里要套两个函数而不是传入两个参数(store,next)呢,就相当于把这个函数柯里化了嘛……后面可以看到用处。
改成ES6的箭头函数
说实话虽然简洁了,但是看起来一点都不直观……可能是我太菜了。嗯,这就是一个中间件的写法了。
可以简单的实现下 applymiddleware
这样就可以最后使用 applymiddleware
深入(meiyou)的理解之后 开始看applymiddleware.js源码
其中用到里 compose 要先看一下
compose.js
这个是函数式编程的一个……思想?应用?
将函数的嵌套调用写成组合 相当于 X)))
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
<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。
勉强看完源码。假装自己理解了这样子。
以上是大佬教程为你收集整理的Redux源码学习笔记全部内容,希望文章能够帮你解决Redux源码学习笔记所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。