概述
Redux 是 JavaScript 狀態(tài)容器捂襟,提供可預測化的狀態(tài)管理方案咬腕。其三大原則為:
- 單一數(shù)據(jù)源 => 整個應用的 state 被儲存在一顆 object tree 中,并且這個 object tree只存在于唯一一個 store 中
- state 是只讀的 => 唯一改變 state 的方法就是觸發(fā) action葬荷,action 是一個用于描述已發(fā)生事件的普通對象
- 使用純函數(shù)來執(zhí)行修改 => 為了描述 action 如何改變 state tree涨共,需要編寫 reducers
純函數(shù): 即相同的輸入,永遠會得到相同的輸出宠漩,而且沒有任何可觀察的副作用
Redux 的核心:
- store => store 是由 Redux 提供的 createStore 生成的煞赢,通過 createStore 方法創(chuàng)建的 store 是一個對象
- dispatch => 調度 state 的更新
- reducer => 處理 state 的更新。reducer 就是一個純函數(shù)哄孤,接收舊的 state 和 action,返回新的 state吹截。指定了應用狀態(tài)的變化如何響應 actions 并發(fā)送到 store 的瘦陈。
- action => action 是把數(shù)據(jù)從應用傳到 store 的有效載荷,它是 store 數(shù)據(jù)的唯一來源波俄。action 是一個對象晨逝,其中包括
type
和payload
屬性。描述變化 - Provider => 全局注冊懦铺,在
<Provider></Provider>
內的組件都可拿到 state - connect => 連接組件和 store
自我實現(xiàn)
代碼捉貌,這部分主要參照了這個系列的相關文章。相關的邏輯可以參照 commit message
官方文檔
- 變化 vs 異步
- React 把處理 state 中數(shù)據(jù)的問題留給了開發(fā)者冬念,Redux -> 處理 state 中的數(shù)據(jù)
- Redux 試圖讓 state 的變化變得可預測
- reducer 合成 -> 每個 reducer 只負責管理全局 state 中它負責的一部分趁窃。每個 reducer 的 state 參數(shù)都不同,分別對應它管理的那部分 state 數(shù)據(jù)急前。開發(fā)一個函數(shù)作為主 reducer醒陆,它調用多個子 reducer 分別處理 state 中的一部分數(shù)據(jù),然后再把這些數(shù)據(jù)合成一個大的單一對象裆针。
- combineReducers API
- store 職責:
- 維持應用的 state
- 提供
getState()
方法獲取 state - 提供
dispatch(action)
方法更新 state - 通過
subscribe(listener)
注冊監(jiān)視器 - 通過
subscribe(listener)
返回的函數(shù)注銷監(jiān)聽器
源碼
類型
-
export type Reducer<S = any, A extends Action = AnyAction> = (state: S | undefined, action: A) => S
;
createStore
類型:Function
-
參數(shù):
- reducer<Reducer>
- 【可選】preloadedState<PreloadedState<S>> => 初始化
state
- 【可選】enhancer<StoreEnhancer<Ext, StateExt>> => 增強器刨摩,用來擴展
store
的功能
返回值:
store<Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext>
源碼分析
- 1 - 10:
preloadedState
和enhancer
不能同時為Function
- 12 - 15:如果
preloadedState
為Function
但是enhancer
為undefined
那么將preloadedState
賦給enhancer
寺晌,并且preloadedState
置為undefined
- 1 - 10:如果
enhancer
不為undefined
,那么enhancer
必須為Function
澡刹,如果是Function
直接返回enhancer(createStore)(reducer, preloadedState)
呻征。這個在applyMiddleware
時具體講解
// TODO: 使用一個 enhancer 作為例子
- 12 - 14: 如果
reducer
不是Function
則拋錯
-
currentReducer
=> 傳入的reducer
-
currentState
=> preloadedState => undefined | 用戶傳入的state
-
currentListeners: (() => void)[] | null
=> [] => 存儲更新函數(shù)的數(shù)組 -
nextListeners
=> [] => 下次dispatch將會觸發(fā)的更新函數(shù)數(shù)組 -
isDispatching<boolean>
=>Lock
,當前是否在dispatch
定義 ensureCanMutateNextListeners
罢浇、getState
陆赋、subscribe
、dispatch
己莺、replaceReducer
和 observable
函數(shù)
- 2:
dispatch
一個ActionTypes.INIT
的action
=> 使得每個reducer
返回初始state
- 4 - 11: 創(chuàng)建
store
變量并返回
dispatch
- 2 - 4:判斷
action
是否是純粹的對象奏甫。即不是Array
|Function
|null
,isPlainObject 定義 - 6 - 8:
action
必須有type
- 10 - 12:如果當前正在
dispatch
則不能dispatch
=> 不可以同時dispatch
兩個action
- 14 - 19:執(zhí)行
currentReducer
凌受,傳入currentState
和action
- 21 - 25:將
nextListeners
賦給currentListeners
和listeners
阵子,之后循環(huán)遍歷執(zhí)行listener
最終return action
getState
getState
方法比較簡單,就是 return currentState
subscribe
- 2 - 8:如果
listener
不是Function
或者isDispatching === true
拋錯 - 10:定義??
isSubscribed
=> 是否已經(jīng)訂閱 - 12: 執(zhí)行
ensureCanMutateNextListeners
函數(shù) - 13:將
listener
推入nextListeners
- 15:
return unsubscribe
函數(shù) - 16 - 20:如果
isSubscribed === false
直接返回胜蛉, 如果isDispatching === true
拋錯 - 22:關??
- 24:執(zhí)行
ensureCanMutateNextListeners
函數(shù) - 25 - 27:在
nextListeners
中刪除listener
函數(shù)挠进,并將currentListeners
置為null
ensureCanMutateNextListeners
對 currentListeners
做了淺拷貝以便于可以在 dispatch
的時候使用 nextListeners
作為臨時的 listener
。這樣可以防止使用者在 dispatch
的時候調用 subscribe/unsubscribe
出現(xiàn) bug誊册。避免相互影響
combineReducers
- 類型:Function
- 參數(shù):reducers
- 返回值:Function
- 使用
combineReducers
例子
源碼分析
- 2 - 13:將用戶傳入的
reducers
做一個shallow copy
领突,并且剔除不是Function
的reducer
。-
finalReducers
為有效的reducer
-
finalReducerKeys
=> 有效reducer
的key
-
- 15 - 20:執(zhí)行
assertReducerShape
函數(shù)案怯,如有Error
將Error
賦值給shapeAssertionError
- 22:返回
combination
函數(shù)
assertReducerShape
遍歷給到的 reducer
- 如果在初始化
action
時返回undefined
君旦,拋錯 - 如果執(zhí)行一個隨機
action
時返回undefined
,拋錯
combination
- 2 - 4:如果
assertReducerShape
函數(shù)執(zhí)行時出錯嘲碱,則拋出錯誤 - 6 - 7: 定義
hasChanged
?? 和nextState
變量 - 8 - 19:遍歷
finalReducerKeys
數(shù)組金砍,previousStateForKey = state[key]
,nextStateForKey = reducer(previousStateForKey, action)
麦锯,如果nextStateForKey
為undefined
拋錯恕稠。將reducer
計算出來的state
存入nextState
中。并且判斷previousStateForKey
和nextStateForKey
是否改變了 - 20 - 21:判斷
finalReducerKeys
和傳入state
的key
的length
是否改變扶欣。最終return hasChanged ? nextState : state
compose
- 類型:Function
- 參數(shù):Function[]
- 返回值:Function
- 使用
compose
例子
源碼分析
- 2 - 5:如果數(shù)組為空鹅巍,返回一個空函數(shù)
- 7 - 9:如果數(shù)組的
length
為1,則返回第一個函數(shù) - 11:對函數(shù)數(shù)組進行
reduce
操作compose(increase, square, add) // (...args) => increase(sqpare(add(...args)))
注意:Array.reduce()
的使用 => 回調函數(shù)第一次執(zhí)行時料祠,accumulator
和 currentValue
取值有兩種情況:如果調用 reduce()
時提供了 initialValue
骆捧,accumulator
取值 initialValue
,currentValue
取數(shù)組中的第一個值髓绽;如果沒有提供 initialValue
凑懂,那么 accumulator
取數(shù)組中第一個值,currentValue
取數(shù)組中的第二個值
applyMiddleware
- 類型:Function
- 參數(shù):Middleware[]
- 返回值:Function
- Middleware:
({getState}) => next => dispatch
=>({getState}) => dispatch => action => any
applyMiddleware的功能:改造dispatch函數(shù)梧宫,產(chǎn)生真假dispatch接谨,而中間件就是運行在假真(dispatchAndLog假和next真)之間的代碼摆碉。
源碼分析
applyMiddleware
函數(shù),直接返回一個函數(shù)脓豪,我們直接看這個函數(shù)即可巷帝。這個函數(shù)的結構是 (createStore) => (reducer, preloadedState?) => store
- 6:執(zhí)行
createStore
函數(shù),獲取store
- 7 - 12:定義
dispatch
函數(shù) - 14 - 17:定義
middlewareAPI
對象扫夜,這里middlewareAPI
只有兩個屬性楞泼,getState
和dispatch
- 18:將用戶傳入的
middleware[]
依次執(zhí)行,并將middlewareAPI
作為參數(shù)傳給各個Middleware
笤闯,Middleware
的例子如下
此時function logger({ getState }) { return next => action => { console.log('will dispatch', action) const returnValue = next(action) console.log('state after dispatch', getState()) return returnValue } }
chain
的值為Middleware
返回值的數(shù)組// chain => [middleware1, middleware2, middleware3] next => action => { console.log('will dispatch', action) const returnValue = next(action) console.log('state after dispatch', getState()) return returnValue }
- 19:
dispatch = compose<typeof dispatch>(...chain)(store.dispatch)
堕阔。使用compose
函數(shù),此時Middleware
中的next === store.dispatch
- 21 - 24:
return store
颗味,這里的dispatch
是 19 行的dispatch
之后看一下 Middleware
使用場所超陆。是作為 applyMiddleware
的參數(shù)使用。applyMiddleware
做為 createStore
的第三個參數(shù)傳入 createStore
浦马。
此時的 enhancer === applyMiddleware
时呀,之后在 createStore
里面直接執(zhí)行
applyMiddleware
,將 createStore
和 reducer
+ preloadedState
傳入 applyMiddleware
晶默,即在 applyMiddleware
代碼中的第 21 行 - 第 24 行的返回值即為 createStore
的返回值
applyMiddleware
做了什么
- 執(zhí)行傳入的
Middleware
谨娜,并將state
作為參數(shù)傳入Middleware
- 更新
dispatch
,這個更新后的dispatch
是用戶傳入的(強化的dispatch
)磺陡,例如上述的logger
例子action => { console.log('will dispatch', action) // 此處的 next === dispatch === store.dispatch === createStore(reducer, preloadedState).dispatch const returnValue = next(action) console.log('state after dispatch', getState()) return returnValue }
注意點
-
enhancer
store 的增強器趴梢。enhancer
是一個高階函數(shù),返回值是一個經(jīng)過包裝的強化的store
币他。applyMiddleware
就是一個enhancer
疑問
Redux 做了什么坞靶?
- 存儲 state
- 增刪改查 state => 可以理解為 state 的變化
- 觸發(fā)更新
middleware 的執(zhí)行順序
Redux 的中間件模型類似于 koa。在 next 前面以及 next圆丹,由外向內依次執(zhí)行。當最里面的 next 執(zhí)行完成之后躯喇,next 后面的代碼會由內向外執(zhí)行辫封。非常類似于 Koa 的洋蔥中間件模型。
dispatch
之后廉丽,Redux 是如何去處理的倦微?
- 執(zhí)行
reducer
更新currentState
- 依次執(zhí)行
listener
函數(shù)
state 中的數(shù)據(jù)被修改之后,訂閱者們如何去收到更新后的數(shù)據(jù)正压?
在 subscribe
中通過 store.getState()
獲取數(shù)據(jù)
applyMiddleware
中 dispatch
為何賦值兩次
第一次賦值表示 => 在 Middleware 創(chuàng)建時欣福,不能進行 dispatch
第二次賦值表示 => 此 dispatch
是一個強化后的 dispatch
middlewareAPI
中的 dispatch
什么時候會被調用
注意使用場景
function logger({ getState, dispatch }) {
// 此時的 dispatch 為拋錯函數(shù),即第一次賦值的函數(shù)焦履,即第 7 - 12 行
return next => {
// 此處的 next === store.dispatch
// 此時的 dispatch === 下面 return 的函數(shù)
return action => {
console.log('will dispatch', action)
const returnValue = next(action)
console.log('state after dispatch', getState())
return returnValue
}
}
}
middlewareAPI
中的 dispatch
為啥要用匿名函數(shù)包裹
let dispatch = () => console.log('Error!');
const obj = {
name: 'dispatch',
dispatch,
}
obj.dispatch();
dispatch = () => console.log('Ready!');
obj.dispatch();
上述的 log 是什么結果呢拓劝?
let dispatch = () => console.log('Error!');
const obj = {
name: 'dispatch',
dispatch: (...args) => dispatch(...args),
}
obj.dispatch();
dispatch = () => console.log('Ready!');
obj.dispatch();
上述的 log 是什么結果呢雏逾?
store.subscribe
為啥要有?? isDispatching
dispatch
執(zhí)行時候會循環(huán)執(zhí)行更新函數(shù),要保證 listeners
數(shù)組在這時候不能被改變
以下代碼作用
答:有了這一層判斷郑临,我們就可以這樣傳:createStore(reducer, initialState, enhancer)
或者這樣:createStore(reducer, enhancer)
栖博,其中 enhancer
還會是 enhancer
。
Redux 優(yōu)勢
- 純函數(shù)厢洞,做測試的時候 easy