redux
是狀態管理庫,與其他框架如 react
是沒有直接關係,所以 redux
可以脫離 react
在別的環境下使用。由於沒有和react
相關邏輯耦合,所以 redux
的原始碼很純粹,目的就是把如何資料管理好。而真正在 react
專案中使用 redux
時,是需要有一個 react-redux
當作聯結器,去連線 react
和 redux
。
沒看 redux
原始碼之前,我覺得看 redux
應該是件很困難的事情,因為當初在學 redux
如何使用的時候就已經被 redux
繁多的概念所淹沒。真正翻看 redux
原始碼的時候,會發現 redux
原始碼內容相當之少,程式碼量也相當少,程式碼質量也相當高,所以是非常值得看的原始碼。
目錄結構
其他目錄都可以不看,直接看 ./src
吧:
.\REDUX\SRC │ applyMiddleware.js │ bindActionCreators.js │ combineReducers.js │ compose.js │ createStore.js │ index.js │ └─utils actionTypes.js isPlainObject.js warning.js
index.js
就是把 applyMiddleware.js
等彙集再統一暴露出去。utils
裡面就放一些輔助函式。所以一共就五個檔案需要看,這五個檔案也就是 redux
暴露出去的五個 API
。
// index.js
import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes'
// 忽略內容
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes
}
複製程式碼
compose.js
這是五個 API
裡唯一一個能單獨拿出來用的函式,就是函數語言程式設計裡常用的組合函式,和 redux
本身沒有什麼多大關係,先了解下函數語言程式設計的一些概念:
程式碼:
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
複製程式碼
其實 compose
函式做的事就是把 var a = fn1(fn2(fn3(fn4(x))))
這種巢狀的呼叫方式改成 var a = compose(fn1,fn2,fn3,fn4)(x)
的方式呼叫。
redux
的 compose
實現很簡潔,用了陣列的 reduce
方法,reduce
的用法可以參照 mdn。
核心程式碼就一句:return funcs.reduce((a,b) => (..args) => a(b(...args)))
我雖然經常寫 reduce
函式,但是看到這句程式碼還是有點懵的,所以這裡舉一個實際的例子,看看這個函式是怎麼執行的:
import {compose} from 'redux'
let x = 10
function fn1 (x) {return x + 1}
function fn2(x) {return x + 2}
function fn3(x) {return x + 3}
function fn4(x) {return x + 4}
// 假設我這裡想求得這樣的值
let a = fn1(fn2(fn3(fn4(x)))) // 10 + 4 + 3 + 2 + 1 = 20
// 根據compose的功能,我們可以把上面的這條式子改成如下:
let composeFn = compose(fn1, fn2, fn3, fn4)
let b = composeFn(x) // 理論上也應該得到20
複製程式碼
看一下 compose(fn1, fn2, fn3, fn4)
根據 compose
的原始碼, 其實執行的就是:
[fn1,fn2,fn3.fn4].reduce((a, b) => (...args) => a(b(...args)))
第幾輪迴圈 | a的值 | b的值 | 返回的值 |
---|---|---|---|
第一輪迴圈 | fn1 | fn2 | (...args) => fn1(fn2(...args)) |
第二輪迴圈 | (...args) => fn1(fn2(...args)) | fn3 | (...args) => fn1(fn2(fn3(...args))) |
第三輪迴圈 | (...args) => fn1(fn2(fn3(...args))) | fn4 | (...args) => fn1(fn2(fn3(fn4(...args)))) |
迴圈最後的返回值就是 (...args) => fn1(fn2(fn3(fn4(...args))))
。所以經過 compose
處理過之後,函式就變成我們想要的格式了。
總結
compose
函式在函數語言程式設計裡很常見。這裡 redux
的對 compose
實現很簡單,理解起來卻沒有那麼容易,主要還是因為對 Array.prototype.reduce
函式沒有那麼熟練,其次就是這種接受函式返回函式的寫法,再配上幾個連續的 =>
,容易看暈。
這是 redux
解讀的第一篇,後續把幾個 API
都講一下。特別是 applyMiddleware
這個 API
有用到這個 compose
來組合中介軟體,也是有那麼一個點比較難理解。