摘要:也可以看我的博客解析源碼解析源碼是狀態(tài)容器,提供可預(yù)測化的狀態(tài)管理。作為全家桶的一份子,可謂說也是名聲響響,在年學(xué)習(xí)想必沒有多少人沒聽過吧。
也可以看我的博客 - 解析 Redux 源碼
解析 Redux 源碼 TIPRedux 是 JavaScript 狀態(tài)容器,提供可預(yù)測化的狀態(tài)管理。
作為 React 全家桶的一份子,Redux 可謂說也是名聲響響,在 2016 年學(xué)習(xí) JavaScript 想必沒有多少人沒聽過吧。
這里,本文不是來教大家如何使用 Redux 的 API 的,這一類的文章已經(jīng)很多,對于 Redux 的介紹和學(xué)習(xí)可以點(diǎn)擊下列鏈接:
官方文檔
官方Github
中文文檔
redux 大法好 —— 入門實(shí)例 TodoList(本人的渣渣文章)
Redux 體小精悍(只有2kB)且沒有任何依賴,因此本文想通過閱讀 Redux 的源碼來學(xué)習(xí) Redux 的使用以及思想。
源碼結(jié)構(gòu)Redux 的源碼結(jié)構(gòu)很簡單,我們可以直接看 src 目錄下的代碼:
.src ├── utils #工具函數(shù) ├── applyMiddleware.js ├── bindActionCreators.js ├── combineReducers.js ├── compose.js ├── createStore.js └── index.js #入口 jsindex.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" function isCrushed() {} if ( process.env.NODE_ENV !== "production" && typeof isCrushed.name === "string" && isCrushed.name !== "isCrushed" ) { warning( "。。。" ) } export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose }
這里的 isCrushed 函數(shù)主要是為了驗(yàn)證在非生產(chǎn)環(huán)境下 Redux 是否被壓縮(因?yàn)槿绻粔嚎s了那么 (isCrushed.name !== "isCrushed") 就是 true),如果被壓縮會給開發(fā)者一個 warn 提示)。
然后就是暴露 createStore combineReducers bindActionCreators applyMiddleware compose 這幾個接口給開發(fā)者使用,我們來逐一解析這幾個 API。
createStore.js這個是 Redux 最主要的一個 API 了,它創(chuàng)建一個 Redux store 來以存放應(yīng)用中所有的 state,應(yīng)用中應(yīng)有且僅有一個 store。
import isPlainObject from "lodash/isPlainObject" import $$observable from "symbol-observable" // 私有 action export var ActionTypes = { INIT: "@@redux/INIT" } export default function createStore(reducer, preloadedState, enhancer) { // 判斷接受的參數(shù)個數(shù),來指定 reducer 、 preloadedState 和 enhancer if (typeof preloadedState === "function" && typeof enhancer === "undefined") { enhancer = preloadedState preloadedState = undefined } // 如果 enhancer 存在并且適合合法的函數(shù),那么調(diào)用 enhancer,并且終止當(dāng)前函數(shù)執(zhí)行 if (typeof enhancer !== "undefined") { if (typeof enhancer !== "function") { throw new Error("Expected the enhancer to be a function.") } return enhancer(createStore)(reducer, preloadedState) } if (typeof reducer !== "function") { throw new Error("Expected the reducer to be a function.") } // 儲存當(dāng)前的 currentReducer var currentReducer = reducer // 儲存當(dāng)前的狀態(tài) var currentState = preloadedState // 儲存當(dāng)前的監(jiān)聽函數(shù)列表 var currentListeners = [] // 儲存下一個監(jiān)聽函數(shù)列表 var nextListeners = currentListeners var isDispatching = false // 這個函數(shù)可以根據(jù)當(dāng)前監(jiān)聽函數(shù)的列表生成新的下一個監(jiān)聽函數(shù)列表引用 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } ... getState ... ... subscribe ... ... dispatch ... ... replaceReducer ... ... observable ... dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
首先定義了一個 ActionTypes 對象,它是一個 action,是一個 Redux 的私有 action,不允許外界觸發(fā),用來初始化 Store 的狀態(tài)樹和改變 reducers 后初始化 Store 的狀態(tài)樹。
createStore然后著重來看 createStore 函數(shù):
它可以接受三個參數(shù),reducer、preloadedState、enhancer:
reducer:是一個函數(shù),返回下一個狀態(tài),接受兩個參數(shù):當(dāng)前狀態(tài) 和 觸發(fā)的 action;
preloadedState:初始狀態(tài)對象,可以很隨意指定,比如服務(wù)端渲染的初始狀態(tài),但是如果使用 combineReducers 來生成 reducer,那必須保持狀態(tài)對象的 key 和 combineReducers 中的 key 相對應(yīng);
enhancer:store 的增強(qiáng)器函數(shù),可以指定為 第三方的中間件,時間旅行,持久化 等等,但是這個函數(shù)只能用 Redux 提供的 applyMiddleware 函數(shù)來生成;
根據(jù)傳入?yún)?shù)的個數(shù)和類型,判斷 reducer 、 preloadedState 、 enhancer;
調(diào)用完函數(shù)它返回的接口是 dispatch subscribe getState replaceReducer 和 [$$observable]
這也是我們開發(fā)中主要使用的幾個接口。
如果 enhancer 參數(shù)傳入并且是個合法的函數(shù),那么就是調(diào)用 enhancer 函數(shù)(傳入 createStore 來給它操作),enhancer 函數(shù)返回的也是一個函數(shù),在這里傳入 reducer 和 preloadedState,并且返回函數(shù)調(diào)用結(jié)果,終止當(dāng)前函數(shù)執(zhí)行;
在 enhancer 函數(shù)里面是如何操作使用的可以看 applyMiddleware 部分;
function getState() { return currentState }
這個函數(shù)可以獲取當(dāng)前的狀態(tài),createStore 中的 currentState 儲存當(dāng)前的狀態(tài)樹,這是一個閉包,這個參數(shù)會持久存在,并且所有的操作狀態(tài)都是改變這個引用,getState 函數(shù)返回當(dāng)前的 currentState。
function subscribe(listener) { // 判斷傳入的參數(shù)是否為函數(shù) if (typeof listener !== "function") { throw new Error("Expected listener to be a function.") } var isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) return function unsubscribe() { if (!isSubscribed) { return } isSubscribed = false ensureCanMutateNextListeners() var index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } }
這個函數(shù)可以給 store 的狀態(tài)添加訂閱監(jiān)聽函數(shù),一旦調(diào)用 dispatch ,所有的監(jiān)聽函數(shù)就會執(zhí)行;
nextListeners 就是儲存當(dāng)前監(jiān)聽函數(shù)的列表,調(diào)用 subscribe,傳入一個函數(shù)作為參數(shù),那么就會給 nextListeners 列表 push 這個函數(shù);
同時調(diào)用 subscribe 函數(shù)會返回一個 unsubscribe 函數(shù),用來解綁當(dāng)前傳入的函數(shù),同時在 subscribe 函數(shù)定義了一個 isSubscribed 標(biāo)志變量來判斷當(dāng)前的訂閱是否已經(jīng)被解綁,解綁的操作就是從 nextListeners 列表中刪除當(dāng)前的監(jiān)聽函數(shù)。
function dispatch(action) { if (!isPlainObject(action)) { throw new Error( "Actions must be plain objects. " + "Use custom middleware for async actions." ) } // 判斷 action 是否有 type{必須} 屬性 if (typeof action.type === "undefined") { throw new Error( "Actions may not have an undefined "type" property. " + "Have you misspelled a constant?" ) } // 如果正在 dispatch 則拋出錯誤 if (isDispatching) { throw new Error("Reducers may not dispatch actions.") } // 對拋出 error 的兼容,但是無論如何都會繼續(xù)執(zhí)行 isDispatching = false 的操作 try { isDispatching = true // 使用 currentReducer 來操作傳入 當(dāng)前狀態(tài)和action,放回處理后的狀態(tài) currentState = currentReducer(currentState, action) } finally { isDispatching = false } var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { var listener = listeners[i] listener() } return action }
這個函數(shù)是用來觸發(fā)狀態(tài)改變的,他接受一個 action 對象作為參數(shù),然后 reducer 根據(jù) action 的屬性 以及 當(dāng)前 store 的狀態(tài)來生成一個新的狀態(tài),賦予當(dāng)前狀態(tài),改變 store 的狀態(tài);
即 currentState = currentReducer(currentState, action);
這里的 currentReducer 是一個函數(shù),他接受兩個參數(shù):當(dāng)前狀態(tài) 和 action,然后返回計(jì)算出來的新的狀態(tài);
然后遍歷 nextListeners 列表,調(diào)用每個監(jiān)聽函數(shù);
function replaceReducer(nextReducer) { // 判斷參數(shù)是否是函數(shù)類型 if (typeof nextReducer !== "function") { throw new Error("Expected the nextReducer to be a function.") } currentReducer = nextReducer dispatch({ type: ActionTypes.INIT }) }
這個函數(shù)可以替換 store 當(dāng)前的 reducer 函數(shù),首先直接把 currentReducer = nextReducer,直接替換;
然后 dispatch({ type: ActionTypes.INIT }) ,用來初始化替換后 reducer 生成的初始化狀態(tài)并且賦予 store 的狀態(tài);
function observable() { var outerSubscribe = subscribe return { subscribe(observer) { if (typeof observer !== "object") { throw new TypeError("Expected the observer to be an object.") } function observeState() { if (observer.next) { observer.next(getState()) } } observeState() var unsubscribe = outerSubscribe(observeState) return { unsubscribe } }, [$$observable]() { return this } } }
對于這個函數(shù),是不直接暴露給開發(fā)者的,它提供了給其他觀察者模式/響應(yīng)式庫的交互操作,具體可看 https://github.com/zenparsing/es-observable
最后執(zhí)行 dispatch({ type: ActionTypes.INIT }),用來根據(jù) reducer 初始化 store 的狀態(tài)。
compose.jscompose 可以接受一組函數(shù)參數(shù),從右到左來組合多個函數(shù),然后返回一個組合函數(shù);
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) }applyMiddleware.js
export default function applyMiddleware(...middlewares) { // 這個返回的函數(shù)就是 enhancer,接受 createStore 函數(shù),再返回一個函數(shù),接受的其實(shí)只有 reducer 和 preloadedState; return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer) var dispatch = store.dispatch var chain = [] // 暴漏 getState 和 dispatch 給 第三方中間價(jià)使用 var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } // 創(chuàng)造第三方中間件使用 middlewareAPI 后返回的函數(shù)組成的數(shù)組 chain = middlewares.map(middleware => middleware(middlewareAPI)) // 結(jié)合這一組函數(shù) 和 dispatch 組成的新的 dispatch,然后這個暴漏給用戶使用,而原有的 store.dispatch 是不變的,但是不暴漏 dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
applyMiddleware 函數(shù)的作用是組合 多個 中間件等等,然后返回一個函數(shù)(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) }
這里 enhancer === applyMiddleware(...);
然后再執(zhí)行 enhancer(createStore) 繼續(xù)之后的操作;
這里 enhancer(createStore) 等同于 (reducer, preloadedState, enhancer) => { ... }
然后再執(zhí)行 enhancer(createStore)(reducer, preloadedState);
再回到 applyMiddleware ,這里調(diào)用了 var store = createStore(reducer, preloadedState, enhancer) ;
如上所見,這里執(zhí)行的時候已經(jīng)沒有 enhancer 參數(shù)了,因此會再次執(zhí)行 createStore 函數(shù)的全部部分,然后得到一個返回的實(shí)例 store;
之后會生成一個新的 dispatch ,先保存下來原生的 dispatch : var dispatch = store.dispatch;
var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) }
這一步是把 store 的 getState 和 dispatch 接口暴露給中間件來操作: chain = middlewares.map(middleware => middleware(middlewareAPI));
最后組合 全部中間件的返回值(函數(shù))chain 和 store.dispatch,然后返回新的 dispatch : dispatch = compose(...chain)(store.dispatch)
這里的 dispatch 并不是原有的 store 的,而是經(jīng)過組合中間件之后新的 dispatch;
最后返回暴露給用戶的接口:
return { ...store, dispatch }
主要還是 store 原有的接口,但是用新的 dispatch 替換了原有的;這個函數(shù)其實(shí)就是根據(jù)中間件和store的接口生成新的 dispatch 然后暴露給用戶。
combineReducers.js這個函數(shù)可以組合一組 reducers(對象) ,然后返回一個新的 reducer 函數(shù)給 createStore 使用。
它接受一組 reducers 組成的對象,對象的 key 是對應(yīng) value(reducer函數(shù))的狀態(tài)名稱;
比如: { userInfo: getUserInfo, list: getList }
userInfo 是根據(jù) getUserInfo 函數(shù)計(jì)算出來的;
那么 store 里面的 state 結(jié)構(gòu)就會是: { userInfo: ..., list: ... }
export default function combineReducers(reducers) { // 根據(jù) reducers 生成最終合法的 finalReducers:value 為 函數(shù) var reducerKeys = Object.keys(reducers) var finalReducers = {} for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i] if (process.env.NODE_ENV !== "production") { if (typeof reducers[key] === "undefined") { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === "function") { finalReducers[key] = reducers[key] } } var finalReducerKeys = Object.keys(finalReducers) if (process.env.NODE_ENV !== "production") { var unexpectedKeyCache = {} } // 驗(yàn)證 reducer 是否合法 var sanityError try { assertReducerSanity(finalReducers) } catch (e) { sanityError = e } // 返回最終生成的 reducer return function combination(state = {}, action) { if (sanityError) { throw sanityError } if (process.env.NODE_ENV !== "production") { var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache) if (warningMessage) { warning(warningMessage) } } var hasChanged = false var nextState = {} for (var i = 0; i < finalReducerKeys.length; i++) { var key = finalReducerKeys[i] var reducer = finalReducers[key] var previousStateForKey = state[key] var nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === "undefined") { var errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } // 遍歷一遍看是否改變,然后返回原有狀態(tài)值或者新的狀態(tài)值 return hasChanged ? nextState : state } }
最終返回一個 combination 也就是真正傳入 createStore 的 reducer 函數(shù);
這是一個標(biāo)準(zhǔn)的 reducer 函數(shù),接受一個初始化狀態(tài) 和 一個 action 參數(shù);
每次調(diào)用的時候會去遍歷 finalReducer (有效的 reducer 列表),獲取列表中每個 reducer 對應(yīng)的先前狀態(tài): var previousStateForKey = state[key];
看到這里就應(yīng)該明白傳入的 reducers 組合為什么 key 要和 store 里面的 state 的 key 相對應(yīng);
然后得到當(dāng)前遍歷項(xiàng)的下一個狀態(tài): var nextStateForKey = reducer(previousStateForKey, action) ;
然后把它添加到整體的下一個狀態(tài): nextState[key] = nextStateForKey
每次遍歷會判斷整體狀態(tài)是否改變: hasChanged = hasChanged || nextStateForKey !== previousStateForKey
在最后,如果沒有改變就返回原有狀態(tài),如果改變了就返回新生成的狀態(tài)對象: return hasChanged ? nextState : state。
bindActionCreators.jsbindActionCreators 函數(shù)可以生成直接觸發(fā) action 的函數(shù);
實(shí)質(zhì)上它只說做了這么一個操作 bindActionFoo = (...args) => dispatch(actionCreator(...args))
因此我們直接調(diào)用 bindActionFoo 函數(shù)就可以改變狀態(tài)了;
接受兩個參數(shù),一個是 actionCreators( actionCreator 組成的對象,key 對于生成的函數(shù)名/或者是一個 actionCreator ),一個是 dispatch, store 實(shí)例中獲??;
function bindActionCreator(actionCreator, dispatch) { return (...args) => dispatch(actionCreator(...args)) } export default function bindActionCreators(actionCreators, dispatch) { // 是一個函數(shù),直接返回一個 bindActionCreator 函數(shù),這個函數(shù)調(diào)用 dispatch 觸發(fā) action if (typeof actionCreators === "function") { return bindActionCreator(actionCreators, dispatch) } if (typeof actionCreators !== "object" || actionCreators === null) { throw new Error( `bindActionCreators expected an object or a function, instead received ${actionCreators === null ? "null" : typeof actionCreators}. ` + `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?` ) } // 遍歷對象,然后對每個遍歷項(xiàng)的 actionCreator 生成函數(shù),將函數(shù)按照原來的 key 值放到一個對象中,最后返回這個對象 var keys = Object.keys(actionCreators) var boundActionCreators = {} for (var i = 0; i < keys.length; i++) { var key = keys[i] var actionCreator = actionCreators[key] if (typeof actionCreator === "function") { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } } return boundActionCreators }總結(jié)
閱讀了一遍 Redux 的源碼,實(shí)在是太精妙了,少依賴,少耦合,純函數(shù)式。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/92349.html
摘要:的中間件主要是通過模塊實(shí)現(xiàn)的。返回的也是一個對象這個其實(shí)就是,各個中間件的最底層第三層的哪個函數(shù)組成的圓環(huán)函數(shù)構(gòu)成的這就是對源碼的一個整體解讀,水平有限,歡迎拍磚。后續(xù)的源碼解讀和測試?yán)涌梢躁P(guān)注源碼解讀倉庫 applyMiddleware源碼解析 中間件機(jī)制在redux中是強(qiáng)大且便捷的,利用redux的中間件我們能夠?qū)崿F(xiàn)日志記錄,異步調(diào)用等多種十分實(shí)用的功能。redux的中間件主要是...
摘要:的返回值是函數(shù),這個函數(shù)經(jīng)調(diào)用,傳入?yún)?shù),之后會在中間件鏈上進(jìn)行傳遞,只要保證每個中間件的參數(shù)是并且將傳遞給下一個中間件。 了解了Redux原理之后,我很好奇Redux中間件是怎么運(yùn)作的,于是選了最常用的redux-thunk進(jìn)行源碼分析。 此次分析用的redux-thunk源碼版本是2.2.0,redux源碼版本是3.7.2。并且需要了解Redux原理 redux中間件都是由redu...
摘要:源碼解析模塊的代碼十分簡練,但是實(shí)現(xiàn)的作用卻是十分強(qiáng)大。只傳遞一個參數(shù)的時候,就直接把這個函數(shù)返回返回組合函數(shù)這就是對源碼的一個整體解讀,水平有限,歡迎拍磚。后續(xù)的源碼解讀和測試?yán)涌梢躁P(guān)注源碼解讀倉庫 compose源碼解析 compose模塊的代碼十分簡練,但是實(shí)現(xiàn)的作用卻是十分強(qiáng)大。redux為何稱為redux?有人說就是reduce和flux的結(jié)合體,而reduce正是comp...
摘要:源碼解析是最核心的模塊。比如,當(dāng)我們需要使用中間件的時候,就會像第三個參數(shù)傳遞一個返回值是一個。后續(xù)的源碼解讀和測試?yán)涌梢躁P(guān)注源碼解讀倉庫 createStore源碼解析 createStore是redux最核心的模塊。這個模塊就是用于創(chuàng)建一個store對象,同時,對外暴露出dispatch,getState,subscribe和replaceReducer方法。(源碼中關(guān)于obse...
摘要:要應(yīng)用于生成環(huán)境必須要用或者,是的進(jìn)化產(chǎn)物,優(yōu)于。我們來看一下他的源碼,從而學(xué)一些東西。里面都是一個一個的模塊,一共個模塊,都導(dǎo)出了一些的方法,比如這個號函數(shù),一個匿名函數(shù),然后導(dǎo)出他寫的方法。整體架構(gòu)就是這樣的。主要用于壓縮的時候。 redux很小的一個框架,是從flux演變過來的,盡管只有775行,但是它的功能很重要。react要應(yīng)用于生成環(huán)境必須要用flux或者redux,red...
摘要:前言的源碼是我閱讀過的一些庫的源碼中,相對簡單的。在更新完成后,同時會更新,并依次執(zhí)行監(jiān)聽者列表。使用新的替換現(xiàn)有的,同時執(zhí)行是隨機(jī)的字符串。會為注冊監(jiān)聽器,監(jiān)聽器存儲在數(shù)組中,返回的函數(shù)則會注銷監(jiān)聽器。使用管道,將逐層的進(jìn)行包裝 showImg(https://segmentfault.com/img/remote/1460000019425043?w=1380&h=810); sh...
閱讀 545·2019-08-30 15:55
閱讀 956·2019-08-29 15:35
閱讀 1210·2019-08-29 13:48
閱讀 1923·2019-08-26 13:29
閱讀 2948·2019-08-23 18:26
閱讀 1261·2019-08-23 18:20
閱讀 2843·2019-08-23 16:43
閱讀 2717·2019-08-23 15:58