摘要:這里還有一個疑問點就是的嵌套,最開始也我不明白,看了源碼才知道,這里返回的也是接受也就是一個所以可以正常嵌套。以作為參數,調用上一步返回的函數以為參數進行調用。
1、本文不涉及redux的使用方法,因此可能更適合使用過 redux 的同學閱讀
2、當前redux版本為4.0.1
3、更多系列文章請看
Redux作為大型React應用狀態管理最常用的工具。雖然在平時的工作中很多次的用到了它,但是一直沒有對其原理進行研究。最近看了一下源碼,下面是我自己的一些簡單認識,如有疑問歡迎交流。
1、createStore結合使用場景我們首先來看一下createStore方法。
// 這是我們平常使用時創建store const store = createStore(reducers, state, enhance);
以下源碼為去除異常校驗后的源碼,
export default function createStore(reducer, preloadedState, enhancer) { // 如果有傳入合法的enhance,則通過enhancer再調用一次createStore if (typeof enhancer !== "undefined") { if (typeof enhancer !== "function") { throw new Error("Expected the enhancer to be a function.") } return enhancer(createStore)(reducer, preloadedState) // 這里涉及到中間件,后面介紹applyMiddleware時在具體介紹 } let currentReducer = reducer //把 reducer 賦值給 currentReducer let currentState = preloadedState //把 preloadedState 賦值給 currentState let currentListeners = [] //初始化監聽函數列表 let nextListeners = currentListeners //監聽列表的一個引用 let isDispatching = false //是否正在dispatch function ensureCanMutateNextListeners() {} function getState() {} function subscribe(listener) {} function dispatch(action) {} function replaceReducer(nextReducer) {} // 在 creatorStore 內部沒有看到此方法的調用,就不講了 function observable() {} //初始化 store 里的 state tree dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
我們可以看到creatorStore方法除了返回我們常用的方法外,還做了一次初始化過程dispatch({ type: ActionTypes.INIT });那么dispatch干了什么事情呢?
/** * dispath action。這是觸發 state 變化的惟一途徑。 * @param {Object} 一個普通(plain)的對象,對象當中必須有 type 屬性 * @returns {Object} 返回 dispatch 的 action */ function dispatch(action) { // 判斷 dispahch 正在運行,Reducer在處理的時候又要執行 dispatch if (isDispatching) { throw new Error("Reducers may not dispatch actions.") } try { //標記 dispatch 正在運行 isDispatching = true //執行當前 Reducer 函數返回新的 state currentState = currentReducer(currentState, action) } finally { isDispatching = false } const listeners = (currentListeners = nextListeners) //遍歷所有的監聽函數 for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() // 執行每一個監聽函數 } return action }
這里dispatch主要做了二件事情
通過reducer更新state
執行所有的監聽函數,通知狀態的變更
那么reducer是怎么改變state的呢?這就涉及到下面的combineReducers了
2、combineReducers回到上面創建store的參數reducers,
// 兩個reducer const todos = (state = INIT.todos, action) => { // .... }; const filterStatus = (state = INIT.filterStatus, action) => { // ... }; const reducers = combineReducers({ todos, filterStatus }); // 這是我們平常使用時創建store const store = createStore(reducers, state, enhance);
下面我們來看combineReducers做了什么
export default function combineReducers(reducers) { // 第一次篩選,參數reducers為Object // 篩選掉reducers中不是function的鍵值對 const reducerKeys = Object.keys(reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (typeof reducers[key] === "function") { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) // 二次篩選,判斷reducer中傳入的值是否合法(!== undefined) // 獲取篩選完之后的所有key let shapeAssertionError try { assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } return function combination(state = {}, action) { let hasChanged = false const nextState = {} // 遍歷所有的key和reducer,分別將reducer對應的key所代表的state,代入到reducer中進行函數調用 for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] // 這里就是reducer function的名稱和要和state同名的原因,傳說中的黑魔法 const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === "undefined") { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } // 將reducer返回的值填入nextState nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 發生改變了返回新的nextState,否則返回原先的state return hasChanged ? nextState : state } }
這里 reducer(previousStateForKey, action)執行的就是我們上面定義的todos和filterStatus方法。通過這二個reducer改變state值。
以前我一直很奇怪我們的reducer里的state是怎么做到取當前reducer對應的數據??吹?b>const previousStateForKey = state[key]這里我就明白了。
這里還有一個疑問點就是combineReducers的嵌套,最開始也我不明白,看了源碼才知道combineReducers()=> combination(state = {}, action),這里combineReducers返回的combination也是接受(state = {}, action)也就是一個reducer所以可以正常嵌套。
看到這初始化流程已經走完了。這個過程我們認識了dispatch和combineReducers;接下來我們來看一下我們自己要怎么更新數據。
用戶更新數據時,是通過createStore后暴露出來的dispatch方法來觸發的。dispatch 方法,是 store 對象提供的更改 currentState 這個閉包變量的唯一建議途徑(注意這里是唯一建議途徑,不是唯一途徑,因為通過getState獲取到的是state的引用,所以是可以直接修改的。但是這樣就不能更新視圖了)。
正常情況下我們只需要像下面這樣
//action creator var addTodo = function(text){ return { type: "add_todo", text: text }; }; function TodoReducer(state = [], action){ switch (action.type) { case "add_todo": return state.concat(action.text); default: return state; } }; // 通過 store.dispatch(action) 來達到修改 state 的目的 // 注意: 在redux里,唯一能夠修改state的方法,就是通過 store.dispatch(action) store.dispatch({type: "add_todo", text: "讀書"});// 或者下面這樣 // store.dispatch(addTodo("讀書"));
也就是說dispatch接受一個包含type的對象??蚣転槲覀兲峁┝艘粋€創建Action的方法bindActionCreators
3、bindActionCreators下面來看下源碼
// 核心代碼,并通過apply將this綁定起來 function bindActionCreator(actionCreator, dispatch) { return function() { return dispatch(actionCreator.apply(this, arguments)) } } export default function bindActionCreators(actionCreators, dispatch) { // 如果actionCreators是一個函數,則說明只有一個actionCreator,就直接調用bindActionCreator if (typeof actionCreators === "function") { return bindActionCreator(actionCreators, dispatch) } // 遍歷對象,然后對每個遍歷項的 actionCreator 生成函數,將函數按照原來的 key 值放到一個對象中,最后返回這個對象 const keys = Object.keys(actionCreators) const boundActionCreators = {} for (let i = 0; i < keys.length; i++) { const key = keys[i] const actionCreator = actionCreators[key] if (typeof actionCreator === "function") { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } } return boundActionCreators }
bindActionCreators的作用就是使用dispatch把 action creator包裹起來,這樣我們就可以直接調用他們了。這個在平常開發中不常用。
4、applyMiddleware最后我們回頭來看一下之前調到的中間件,
import thunkMiddleware from "redux-thunk"; // 兩個reducer const todos = (state = INIT.todos, action) => { // .... }; const filterStatus = (state = INIT.filterStatus, action) => { // ... }; const reducers = combineReducers({ todos, filterStatus }); // 這是我們平常使用時創建store const store = createStore(reducers, state, applyMiddleware(thunkMiddleware));
為了下文好理解這個放一下redux-thunk的源碼
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === "function") { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
可以看出thunk返回了一個接受({ dispatch, getState })為參數的函數
下面我們來看一下applyMiddleware源碼分析
export default function applyMiddleware(...middlewares) { return createStore => (...args) => { const store = createStore(...args) let dispatch = () => { throw new Error( `Dispatching while constructing your middleware is not allowed. ` + `Other middleware would not be applied to this dispatch.` ) } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } // 每個 middleware 都以 middlewareAPI 作為參數進行注入,返回一個新的鏈。此時的返回值相當于調用 thunkMiddleware 返回的函數: (next) => (action) => {} ,接收一個next作為其參數 const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 并將鏈代入進 compose 組成一個函數的調用鏈 // compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/h都是chain中的函數對象。 // 在目前只有 thunkMiddleware 作為 middlewares 參數的情況下,將返回 (next) => (action) => {} // 之后以 store.dispatch 作為參數進行注入注意這里這里的store.dispatch是沒有被修改的dispatch他被傳給了next; dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } } // 定義一個代碼組合的方法 // 傳入一些function作為參數,返回其鏈式調用的形態。例如, // compose(f, g, h) 最終返回 (...args) => f(g(h(...args))) export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } else { const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) } }
我第一眼看去一臉悶逼,咋這么復雜。但是靜下心來看也就是一個三級柯里化的函數,我們從頭來分析一下這個過程
// createStore.js if (typeof enhancer !== "undefined") { if (typeof enhancer !== "function") { throw new Error("Expected the enhancer to be a function.") } return enhancer(createStore)(reducer, preloadedState) }
也就是說,會變成這樣
applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)
applyMiddleware(thunkMiddleware)
applyMiddleware接收thunkMiddleware作為參數,返回形如(createStore) => (...args) => {}的函數。
applyMiddleware(thunkMiddleware)(createStore)
以 createStore 作為參數,調用上一步返回的函數(...args) => {}
applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)
以(reducer, preloadedState)為參數進行調用。 在這個函數內部,thunkMiddleware被調用,其作用是監測type是function的action
因此,如果dispatch的action返回的是一個function,則證明是中間件,則將(dispatch, getState)作為參數代入其中,進行action 內部下一步的操作。否則的話,認為只是一個普通的action,將通過next(也就是dispatch)進一步分發
也就是說,applyMiddleware(thunkMiddleware)作為enhance,最終起了這樣的作用:
對dispatch調用的action進行檢查,如果action在第一次調用之后返回的是function,則將(dispatch, getState)作為參數注入到action返回的方法中,否則就正常對action進行分發,這樣一來我們的中間件就完成嘍~
因此,當action內部需要獲取state,或者需要進行異步操作,在操作完成之后進行事件調用分發的話,我們就可以讓action 返回一個以(dispatch, getState)為參數的function而不是通常的Object,enhance就會對其進行檢測以便正確的處理
到此redux源碼的主要部分講完了,如有感興趣的同學可以去看一下我沒講到的一些東西轉送門redux;
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109079.html
摘要:的中間件主要是通過模塊實現的。返回的也是一個對象這個其實就是,各個中間件的最底層第三層的哪個函數組成的圓環函數構成的這就是對源碼的一個整體解讀,水平有限,歡迎拍磚。后續的源碼解讀和測試例子可以關注源碼解讀倉庫 applyMiddleware源碼解析 中間件機制在redux中是強大且便捷的,利用redux的中間件我們能夠實現日志記錄,異步調用等多種十分實用的功能。redux的中間件主要是...
摘要:源碼解析是最核心的模塊。比如,當我們需要使用中間件的時候,就會像第三個參數傳遞一個返回值是一個。后續的源碼解讀和測試例子可以關注源碼解讀倉庫 createStore源碼解析 createStore是redux最核心的模塊。這個模塊就是用于創建一個store對象,同時,對外暴露出dispatch,getState,subscribe和replaceReducer方法。(源碼中關于obse...
摘要:本文并不逐行地對源碼進行細致分析,不如說是基于以下幾個問題,對源碼進行大致的掃覽。我們已經知道,中,允許用戶注冊監聽器,這些監聽器會在每次執行結束后遍歷觸發。省略一些無關代碼其中,是為了在嵌套的中嵌套執行。 react-redux 源碼解讀 [TOC] 前置知識 閱讀本篇文章前,請先確認你是否了解以下知識: react redux 高階組件 react diff 機制 其中高階組件...
摘要:源碼解析模塊的代碼十分簡練,但是實現的作用卻是十分強大。只傳遞一個參數的時候,就直接把這個函數返回返回組合函數這就是對源碼的一個整體解讀,水平有限,歡迎拍磚。后續的源碼解讀和測試例子可以關注源碼解讀倉庫 compose源碼解析 compose模塊的代碼十分簡練,但是實現的作用卻是十分強大。redux為何稱為redux?有人說就是reduce和flux的結合體,而reduce正是comp...
閱讀 2838·2023-04-26 02:23
閱讀 1585·2021-11-11 16:55
閱讀 3151·2021-10-19 11:47
閱讀 3362·2021-09-22 15:15
閱讀 1979·2019-08-30 15:55
閱讀 1037·2019-08-29 15:43
閱讀 1295·2019-08-29 13:16
閱讀 2196·2019-08-29 12:38