目前公司是基於React
的技術棧,而我本身對於React
的理解只限於應付項目業務開發而已,並且公司項目基本都是依賴umi
框架機制,致使新人甚至對一些最基本的知識點都不瞭解就開始着手開發,好比react-router
/react-redux
javascript
大佬造的輪子用多了也是會有負面影響,爲了避免讓本身作一條只會用別人東西的鹹魚,決定去了解下redux
的原理,由於redux
源碼相對比較簡潔並且經過redux
大體都能瞭解其餘的狀態管理工具的設計思路,一通則百通,無外乎這個道理,之後就算有幾百個輪子均可以很快理解java
在瞭解源碼以前先能夠仔細看下Redux
的總體流程圖(感謝若川大佬的圖,這裏冒昧借用下),若是能簡單看懂這張圖,那麼redux
的源碼學習將會很是快速react
爲了瞭解源碼,就必需要學會調試代碼,如今咱們去 redux@4.0.4 拷貝redux
的代碼到本地git
git clone https://github.com/reduxjs/redux.git
複製代碼
能夠很清晰的看到redux
是使用rollup
打包,爲了能搞更好地調試,給它加上sourcemap
配置github
//packages.json
"scripts": {
"build": "rollup -c -m",
...
}
複製代碼
執行yarn build
命令,能夠看到生成了es
/dist
/lib
對應不一樣的環境編程
接下來我是直接使用的官方examples
文件夾裏的async
例子,直接把es
文件複製進去,在examples/async/index.js
裏更改引入json
// import { createStore, applyMiddleware } from 'redux'
import { createStore, applyMiddleware } from './es/redux.js'
複製代碼
不少文章都是從createStore
開始介紹,我按照本身的思路,從combineReducers
開始一步一步理解redux
的機制redux
先看下一個redux
官方給出的簡單例子: /reducesc/index.js
promise
這裏定義了postsBySubreddit
,selectedSubreddit
兩個函數,即爲兩個reducer
, 也是咱們項目中都會存在的,並且只會比它多。而在index.js
中,須要把整合的rducer
傳入createStore
中,下面咱們看看 combineReducers
對這兩個reducer
作了什麼bash
能夠很清晰得看到combineReducers
獲取兩個reducer
函數,通過簡單地校驗,生成了相似{ [function.name]: function }
的鍵值對象, 並存放在閉包中(finalReducers
), 最後返回了一個combination
函數,用來傳入createStore
中做爲形參,而combination
具體作了什麼下面會講
當reducer
做爲參數傳入createStore
中以後,createStore
函數執行返回核心的store
對象,順着思路來看下createStore
中作了什麼
store
就是返回
dispatch
、
subscribe
、
getState
、
replaceReducer
等方法。工做中用到過
redux
的應該很是熟悉。
如今咱們具體看下對傳進來的reducer
作了什麼,其實上面都是一些方法的定義,重點要注意這句
// When a store is created, an "INIT" action is dispatched so that every
// reducer returns their initial state. This effectively populates
// the initial state tree.
dispatch({ type: ActionTypes.INIT })
複製代碼
這裏單獨執行一個distpatch
方法,是爲了初始化生成一個state tree
, 也就是生成一個包含store
裏面全部state
的樹形結構對象,接下來主要看下dispatch
作了什麼, 而咱們以前傳入的reducer
也就在這裏起了做用
這裏咱們須要關注的重點就是currentReducer
(其實就是上文傳過來的combination
函數),這裏調用了這個函數,並傳入state
/action
這裏能夠看出combination
拿到最初的state
和action
, 先作了簡單的判斷,以後也就是這裏的一個問題:遍歷執行全部的reducer
來改變state
固然,最初的時候是造成了基本的樹形結構state
, 可是以後的每一步dispatch
都要去遍歷全部的reducer
獲得新的state
進行比較再返回最終的state
, 保存進createStore
裏的currentState
中,能夠經過store
暴露的getState
方法獲取當前的state
subscribe
其實就是訂閱發佈模式
的一種實現吧,我是這麼理解的,每當 dispatch action
的時候就會執行,state
樹中的一部分可能已經變化。你能夠在回調函數裏調用 getState()
來拿到當前 state
。它的代碼也很簡單
咱們在index.js
中加入,而後開始調試,它會走到createStore
中暴露的subscribe
方法裏
debugger;
store.subscribe(() => console.log(7))
複製代碼
subscribe
方法會把listener
加入到nextListeners
中,至關於發佈訂閱模式
中的消息隊列
,以後在dispatch
函數執行的時候,遍歷nextListeners
出發listener
函數,具體代碼在dispatch
中查看,這裏不作詳述了
以上是對基本的redux
機制作了簡單的分析理解,咱們會發現這些功能還不足以徹底承載咱們的業務,因此redux
自己引入中間件
的概念對自己功能進行擴展。社區有不少中間件
的開源庫,包括redux-thunk
,redux-promise
,redux-saga
等等等等,學是學不完的,但仍是那句話,一通百通的道理......
若是咱們要加入中間件,能夠在createStore
中加入參數applyMiddleware(...middlewares)
, 這裏我以redux-thunk
爲例
import { createStore, applyMiddleware } from './es/redux.js'
import thunk from 'redux-thunk'
import reducer from './reducers'
const store = createStore(
reducer,
applyMiddleware(thunk)
)
複製代碼
看一下applyMiddleware
函數具體作了什麼
這裏applyMiddleware
用了柯里化
的理念,按上面的例子來講第一個參數就是thunk
,獲取數據以後會在createStore
裏調用
接下來看下createStore
中對於中間件的處理邏輯
這裏的enhancer
函數就是上方的applyMiddleware(thunk)
返回函數,再次傳入createStore
以及初始化的reducer
和preloadedState
, 這表示又再次走到了applyMiddleware
函數裏面
這裏跟上面同樣生成了store
對象,賦給middleware
返回chain
,這裏最難理解的就是compose
方法,也是中間件機制的關鍵所在
這裏其實也是函數式編程
的一個組合
的理念,把函數從右至左進行組合,能夠理解爲上個函數的返回就是下個函數的參數,若是仍是不明白這個reduce
代碼可能不理解,那咱們慢慢來理解它
首先先看個例子:
很明顯結果是108
, 執行順序依次是multiply
,add
,minus
,那接下來咱們把它寫成通用的函數
redux
中的compose
跟這很相似,可是區別在於它返回的是雙層函數,即next(...)
函數,這裏是中間件機制最難理解的地方,它就是經過這種方式來實現洋蔥模型的
爲了理解compose
的組合機制,咱們先回來看下react-thunk
的代碼
接來下咱們再本身實現一個簡單的middleware
在看下上面的調試的截圖,多箇中間件時,chain
則等於[next => action => ..., next => action => ...]
, 再進入compose
函數,獲得的結構就是(...args) => a(b(...args))
, 這時又傳入store.dispatch
(即next
函數)
store.dispatch
傳入以後,next
被替代爲store.dispatch
而返回了action => {}
函數,這個函數又做爲下一個中間件的next
函數dispatch(action)
, 中間件接收到了action
,則開始調用剛傳入的 next
函數指向外部next
直至store.dispatch
如今咱們根據redux-thunk
具體的例子來理解:
export const requestPosts = subreddit => ({
type: REQUEST_POSTS,
subreddit
})
const fetchPosts = subreddit => (dispatch, getState) => {
dispatch(requestPosts(subreddit))
}
// dispatch action in redux-thunk
dispatch(fetchPosts(subreddit))
複製代碼
根據react-thunk
的代碼,fetchPosts(subreddit)
的返回值就是action
, 而action
是函數, 則return action(dispatch, getState, extraArgument);
, 根據例子上的第二個返回函數(dispatch, getState) => {}
由此而來
這裏的action(dispatch, ...)
中的dispatch
則是compose
組合返回的, requestPosts(subreddit)
執行後再次進入react-thunk
中間件,因爲不是函數則執行next(action)
,這時候若是有其餘的中間件,則根據compose
繼續向左執行
經過以上對Redux
源碼的簡單解析,大體理解了它自己的內部機制, 如今再到頭部看那張流程圖是否理解得更加透徹了呢