redux 版本號: "redux": "4.0.5"
redux 做爲一個十分經常使用的狀態容器庫, 你們都應該見識過, 他很小巧, 只有 2kb, 可是珍貴的是他的 reducer
和 dispatch
這種思想方式react
在閱讀此文以前, 先了解/使用 redux 相關知識點, 才能更好地閱讀本文git
入口是在 redux/src/index.js
中, 在入口文件中只作了一件事件, 就是引入文件, 集中導出
如今咱們根據他導出的方法, 來進行分析github
這個是 redux 最主要的 APIredux
搭配這使用方法一塊兒, 能夠更好的瀏覽源碼segmentfault
createStore(reducer, [preloadedState], [enhancer])
api
他的主要功能就是建立一個 store, 將 reducer
轉換到 store
數組
一共可接受三個參數:promise
enhancer(createStore)(reducer, preloadedState)
, enhancer
createStore
, 一樣地他也須要 return
一個相似於 createStore
的結果, 也就是說, 只有咱們返回的是 一個像 createStore
的東西,enhancer
和 applyMiddleware
的文章 https://juejin.cn/post/684490...// 簡單的例子: function counterReducer(state, action) { switch (action.type) { case 'counter/incremented': return {value: state.value + 1} case 'counter/decremented': return {value: state.value - 1} default: return state } } let store = createStore(counterReducer, { value: 12345 })
createStore
返回的固然是一個 store
, 他有本身的 api
app
返回應用程序的當前狀態樹異步
const state = store.getState()
這個其實不用我多說, 會 redux
的都應該知道這個
store.dispatch({type: 'counter/incremented'})
添加一個監聽器, 每當 action
dispatch
的時候, 都會調用 listener
, 在 listener
中可使用 getState
來獲取當前的狀態樹
const unsubscribe = store.subscribe(() => { console.log('listener run') const current = store.getState() if (current.value === 12350) { unsubscribe() } })
展現一個場景, 監聽事件, 當達到某個條件以後, 解除監聽事件
使用一個 reducer
替換當前的 reducer,對於 reducers
實現動態加載,也能夠爲 Redux
實現熱重載機制
createStore
文件是在 redux/src/createStore.js
中, 他接受的參數就是上面咱們說的那三個, 返回的也就是 store
首先是一段參數的判斷, 以及 enhancer
的返回
// 爲了適配 createStore(reducer, enhancer) 的狀況 if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } // enhancer 的使用場景 return enhancer(createStore)(reducer, preloadedState) }
接下來定義一些變量和函數
let currentReducer = reducer let currentState = preloadedState let currentListeners = [] let nextListeners = currentListeners let isDispatching = false // 若是相等 , 作了一層淺拷貝 將 currentListeners 同步到 nextListeners 中 // 避免相互影響 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } }
function getState() { // isDispatching 默認爲 false, 表示當前 store 是否正在 dispatch if (isDispatching) { throw new Error('//...') } // 直接返回當前 state , 默認爲入參 preloadedState return currentState }
// 忽略了錯誤判斷 function subscribe(listener) { let isSubscribed = true // 同步 nextListeners , currentListeners ensureCanMutateNextListeners() // 將 listener 加入 nextListeners nextListeners.push(listener) // 返回解除監聽函數 return function unsubscribe() { if (!isSubscribed) { // 若是 isSubscribed 已經爲 false 了 則 return // 狀況 1, 已經執行過unsubscribe了一次 return } // flag isSubscribed = false // 同步 nextListeners , currentListeners ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) // 搜索 監聽器, 刪除 currentListeners = null } }
function dispatch(action) { // 省略了 action 的 錯誤拋出 // 總結: action 必須是一個 Object 且 action.type 必須有值存在 // 若是當前正在 isDispatching 則拋出 錯誤(通常來講不存在 try { isDispatching = true // 執行 reducer, 須要注意的是 currentReducer 不能爲異步函數 currentState = currentReducer(currentState, action) } finally { isDispatching = false } // 將 nextListeners 賦值給 currentListeners 執行 nextListeners 裏面的監聽器 const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } // 返回 action return action }
function replaceReducer(nextReducer) { // 若是 nextReducer 不是函數則拋出錯誤 // 直接替換 currentReducer = nextReducer // 相似 ActionTypes.INIT. 替換值 dispatch({type: ActionTypes.REPLACE}) }
還有一個額外的 observable
對象:
// 一個 Symbol.observable 的 polyfill import $$observable from 'symbol-observable' function observable() { // subscribe 就是 store.subscribe const outerSubscribe = subscribe return { subscribe(observer) { // 若是 observer 不是對象或者爲 null 則拋出錯誤 function observeState() { if (observer.next) { // next 的入參爲 固然 reducer 的值 observer.next(getState()) } } observeState() // 添加了監聽 const unsubscribe = outerSubscribe(observeState) return {unsubscribe} }, // 獲取到當前 對象, $$observable 值是一個 symbol [$$observable]() { return this } } }
這裏使用了 tc39
裏未上線的標準代碼 Symbol.observable
, 若是你使用或者瞭解過 rxjs
, 那麼這個對於你來講就是很簡單的, 若是不熟悉,
能夠看看這篇文章: https://juejin.cn/post/684490...
function createStore() { // 省略 // 初始化了下值 dispatch({type: ActionTypes.INIT}) // 返回 return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
// 能夠接受多個 reducer, 實現一種 module 的功能 rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer}) // 返回值 { potato: { // 某些屬性 } , tomato: { // 某些屬性 } } const store = createStore(rootReducer, { potato: { // 初始值 } })
有一點須要注意的是, reducer 都是須要默認值的,如:
function counterReducer(state = {value: 0}, action) { //... }
先看 combineReducers
執行以後產生了什麼
function combineReducers(reducers) { // 第一步是獲取 key, 他是一個數組 const reducerKeys = Object.keys(reducers) const finalReducers = {} // 遍歷 reducers, 賦值到 finalReducers 中, 確保 reducer 是一個函數, 不是函數則過濾 for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] // 省略 reducers[key] 若是是 undefined 拋出錯誤 if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } // finalReducerKeys 通常來講是和 reducerKeys 相同的 const finalReducerKeys = Object.keys(finalReducers) //定義了兩個遍歷 let unexpectedKeyCache let shapeAssertionError try { // 此函數後面會詳細講述 // 答題做用就是確認 finalReducers 中都是有初始值的 assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } //... }
再看他又返回了什麼(記住結果必然也是一個 reducer)
function combineReducers(reducers) { //... return function combination(state = {}, action) { // 若是 assertReducerShape 出錯則拋出錯誤 if (shapeAssertionError) { throw shapeAssertionError } // 忽略非 production 代碼 // 預先定義一些變量 let hasChanged = false const nextState = {} // 循環 finalReducerKeys for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] // 這是一開始的值 const nextStateForKey = reducer(previousStateForKey, action) // 經過 reducer 再次生成值 // 若是 nextStateForKey === undefined 則再次拋出異常 // 給 nextState 賦值 nextState[key] = nextStateForKey // 判斷是否改變 (初始值是 false) 判斷簡單的使用 !== 來比較 // 若是已經爲 true 就一直爲 true 了 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 循環後再次對 true 作出判斷 // 是否少了 reducer 而形成誤判 hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length // 若是改變了 返回新值, 不然返回舊值 return hasChanged ? nextState : state } }
combineReducers
基本就是上述兩個函數的結合, 經過循環遍歷全部的 reducer 計算出值
function assertReducerShape(reducers) { Object.keys(reducers).forEach(key => { // 遍歷參數裏的 reducer const reducer = reducers[key] //執行初始操做 產生初始值都有初始值 const initialState = reducer(undefined, {type: ActionTypes.INIT}) //... 若是 initialState 是 undefined 則拋出錯誤 // 若是 reducer 執行未知操做 返回的是 undefined 則拋出錯誤 // 情景: 當前 reducer 使用了 ActionTypes.INIT 來產生值, 這可以經過上一步 // 但在這一步就會被檢測出來 if ( typeof reducer(undefined, { type: ActionTypes.PROBE_UNKNOWN_ACTION() }) === 'undefined' ) { //... 拋出錯誤 } }) }
這裏咱們能夠知道一點, 全部 reducer 咱們都必需要有一個初始值, 並且他不能是 undefined, 能夠是 null
這裏須要先講 compose 的使用 才能順帶過渡到下面
就如他的名字, 是用來組合函數的, 接受刀哥函數, 返回執行的最終函數:
// 這裏經常使用來 連接多箇中間件 const store = createStore( reducer, compose(applyMiddleware(thunk), DevTools.instrument()) )
他的源碼也很簡單:
function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } // 上面都是 控制, 參數數量爲 0 和 1 的狀況 // 這裏是重點, 將循環接收到的函數數組 return funcs.reduce((a, b) => (...args) => a(b(...args))) }
咱們將 reduce
的運行再度裝飾下:
// reduce 中沒有初始值的時候, 第一個 `prevValue` 是取 `funcs[0]` 的值 funcs.reduce((prevValue, currentFunc) => (...args) => prevValue(currentFunc(...args)))
reducer 返回的就是 這樣一個函數 (...args) => prevValue(currentFunc(...args))
, 一層一層嵌套成一個函數
舉一個簡單的輸入例子:
var foo = compose(val => val + 10, () => 1)
foo 打印:
(...args) => a(b(...args))
執行 foo()
, 返回 11
applyMiddleware
是使用在 createStore
中的 enhancer
參數來加強 redux
的做用
可兼容多種三方插件, 例如 redux-thunk
, redux-promise
, redux-saga
等等
這裏使用官網的一個例子做爲展現:
function logger({getState}) { // next 就是真正的 store.dispatch return next => action => { console.log('will dispatch', action) const returnValue = next(action) console.log('state after dispatch', getState()) return returnValue } } const store = createStore(rootReducer, { counter: {value: 12345} }, applyMiddleware(logger))
default function applyMiddleware(...middlewares) { return createStore => (...args) => { // 由於使用了 enhancer 參數, 他的內部沒有 createStore 的東西, 因此這裏須要從新 createStore const store = createStore(...args) let dispatch = () => { // 在中間件中 不容許使用 dispatch throw new Error( // 省略報錯... ) } // 這是要傳遞的參數 const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } // 從新 map 全部 middlewares 返回須要的結果 const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 這裏就是咱們上面的 compose 相關的代碼, 返回的結果 再次執行 獲得真正的 dispatch dispatch = compose(...chain)(store.dispatch) // 返回 store 和 dispatch return { ...store, dispatch } } }
這裏咱們須要從新捋一捋函數的執行, 中間件以上述的 logger
爲例子
applyMiddleware(logger)
-> 返回的是一個函數(createStore) => (...args) => {/*省略*/}
我把他記爲中間件函數 1
也就是說 applyMiddleware(logger)
=== (createStore) => (...args) => {/*省略*/}
這個函數將在 createStore
中使用 enhancer(createStore)(reducer, preloadedState)
這裏的 enhancer
就是中間件函數 1 經過 createStore
的執行咱們能夠發現store
=== createStore(reducer, preloadedState, enhancer)
=== enhancer(createStore)(reducer, preloadedState)
=== applyMiddleware(logger)(createStore)(reducer, preloadedState)
=== ((createStore) => (...args) => {/*省略*/})(createStore)(reducer, preloadedState)
=== 中間件函數 1 中的{/*省略*/}
返回結果 經過這一層的推論咱們能夠得出 store
=== 中間件函數 1中的 {/*省略*/}
返回結果
這個 API 主要是用來方便 dispatch
的 他接受 2 個參數 , 第一個是對象或函數, 第二個就是 dispatch 返回值的類型很第一個參數相同
首先咱們要定義建立 action
的函數
function increment(value) { return { type: 'counter/incremented', payload: value } } function decrement(value) { return { type: 'counter/decremented', payload: value } }
使用狀況 1:
function App(props) { const {dispatch} = props // 由於在 hooks 中使用 加上了 useMemo const fn = useMemo(() => bindActionCreators(increment, dispatch), [dispatch]) return ( <div className="App"> <div> val: {props.value} </div> <button onClick={() => { fn(100) }}>plus </button> </div> ); }
使用狀況 2:
function App(props) { const {dispatch} = props const fn = useMemo(() => bindActionCreators({ increment, decrement }, dispatch), [dispatch]) // 若是想用 decrement 也是這樣調用 fn.decrement(100) return ( <div className="App"> <div> val: {props.value} </div> <button onClick={() => { fn.increment(100) }}>plus </button> </div> ); }
function bindActionCreator(actionCreator, dispatch) { return function () { // 執行 dispatch(actionCreator()) === dispatch({type:''}) return dispatch(actionCreator.apply(this, arguments)) } } function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') { // 若是是函數直接執行 bindActionCreator return bindActionCreator(actionCreators, dispatch) } if (typeof actionCreators !== 'object' || actionCreators === null) { throw new Error(/*省略*/) } // 定義變量 const boundActionCreators = {} // 由於是對象 循環遍歷, 可是 for in 效率太差 for (const key in actionCreators) { const actionCreator = actionCreators[key] // 過濾 if (typeof actionCreator === 'function') { // 和函數一樣 執行 bindActionCreator 而且賦值到 boundActionCreators 中 boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } } return boundActionCreators }
bindActionCreators
的源碼相對簡單一點, 理解起來相對也容易不少
redux 中設計的不少地方都是很巧妙的,而且短小精悍, 值得你們做爲首次源碼閱讀的選擇
若是我講的有什麼問題, 還望不吝指教
相關文章: react-redux 源碼淺析
本文代碼倉庫: https://github.com/Grewer/rea...
參考文檔: