網站首頁 編程語言 正文
一、Redux是什么
眾所周知,Redux最早運用于React框架中,是一個全局狀態管理器。Redux解決了在開發過程中數據無限層層傳遞而引發的一系列問題,因此我們有必要來了解一下Redux到底是如何實現的?
二、Redux的核心思想
Redux主要分為幾個部分:dispatch、reducer、state。
我們著重看下dispatch,該方法是Redux流程的第一步,在用戶界面中通過執行dispatch,傳入相對應的action對象參數,action是一個描述類型的對象,緊接著執行reducer,最后整體返回一個store對象,我們來看下這部分的源碼:
// 主函數createStore
// 返回一個store對象
export default function createStore(reducer, preloadedState, enhancer) {
// 增強器
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.')
}
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
// 獲取最終的state
function getState() {
if (isDispatching) {
throw new Error(
'You may not call store.getState() while the reducer is executing. ' +
'The reducer has already received the state as an argument. ' +
'Pass it down from the top reducer instead of reading it from the store.'
)
}
return currentState
}
// dispatch
// 參數action
function dispatch(action) {
// 校驗傳入的action
// action必須是個對象,否則拋出錯誤信息
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
// 檢驗action對象的必要屬性
// type屬性是action對象必要的屬性
// 如果傳入的action沒有type屬性,則拋出錯誤信息
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
// 執行傳入的reducer函數
// 返回state,給currentState賦值
currentState = currentReducer(currentState, action)
} finally {
// 一個dispatch執行完,還原狀態
isDispatching = false
}
// 執行訂閱函數隊列
// dispatch執行的同時會一并執行訂閱隊列
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
// 返回action
return action
}
// 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,做初始化
dispatch({ type: ActionTypes.INIT })
// 返回一個store對象
return {
dispatch,
subscribe,
getState,
...
}
}
通過源碼我們可以基本清楚,通過執行createStore方法,最終會返回一個store對象,該對象主要暴露幾個屬性,我們主要關注比較常用的:dispatch、getState、getState,看下實際用例:參考React實戰視頻講解:進入學習
import createStore from 'redux'
// 創建一個reducer
function reducer(state={}, action) {
switch(action.type) {
case 'TEST':
return {
...state,
test: 'test success'
}
}
}
// 返回store
const store = createStore(reducer, initState={})
// 執行dispatch
store.dispatch({
type: 'TEST'
})
const state = store.getState() // 返回 {test: 'TEST'}
三、Redux中間件原理
接下來我們來探討Redux的另一個重要組成部分—中間件。什么是Redux的中間件?Redux中間件其實是通過重寫createStore來增強和擴展原來的dispatch方法,使其能夠在執行dispatch的同時可以同步執行其它方法,比如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;
下載了中間件,那么我們來看下如何使用中間件:
import createStore, {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->applyMiddleWare} from 'reduximport reduxThunk from 'redux-thunk'// 創建一個reducerfunction reducer(state={}, action) { switch(action.type) { case 'TEST': return { ...state, test: 'test success' } }}// 返回store// 中間件作為applyMiddleWare的參數傳入createStoreconst store = createStore(reducer, initState={},applyMiddleWare(reduxThunk))
我們會發現,中間件的使用方式是用applyMiddleWare把中間件作為參數傳入createStore中,那么applyMiddleWare是如何實現的?在這之前我們先看下createStore方法的第三個參數是什么,我們回看下createStore源碼:
export default function createStore(reducer, preloadedState, enhancer) {
...
// 增強器
// 第三個參數是enhancer,也就是我們傳入的applyMiddleWare
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 在這里return了enhancer結果
// 傳入了createStore,reducer,preloadedState
// 實際上是重寫了createStore
return enhancer(createStore)(reducer, preloadedState)
}
...
}
看完了enhancer的實際作用,我們可以弄清楚applyMiddleWare的實現原理,請看源碼:
import compose from './compose'
// 傳入middlewares中間件
export default function applyMiddleware(...middlewares) {
// 閉包嵌套返回2個方法
return createStore => (...args) => {
// 返回store
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.'
)
}
// 返回一個對象
// 包含getState方法和dispatch方法
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args) // 返回一個全新的dispatch方法,不污染原來的dispatch
}
// 執行中間件第一層方法
// 回顧下中間的格式:({getState, dispatch}) => next => action => next(action)
// 這里會比較繞
const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 返回一個中間件的函數集合[next => action => next(action), next => action => next(action)]
// 使用compose聚合chain函數集合
// 返回新的dispatch
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
這里可能會讓人很疑惑,不大清楚的童鞋可以先看下中間件的規范寫法,這里還有一個重要的函數compose,我們來看下compose怎么處理chain函數集合的,請看源碼:
/** * Composes single-argument functions from right to left. The rightmost * function can take multiple arguments as it provides the signature for * the resulting composite function. * * @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). */
// 傳入聚合函數集合
// 集合為:[next => action => next(action), next => action => next(action)]
// 返回一個新的函數: (arg) => arg
export default function compose(...funcs) {
// 判斷如果沒有則返回一個新函數
// 可以聯想一下dispatch的定義
// function dispatch(action) {
...
return action
}
if (funcs.length === 0) {
return arg => arg
}
// 判斷如果只有一個中間件,則直接返回第一個
if (funcs.length === 1) {
return funcs[0]
}
// 這里用了reduce函數
// 把后一個的中間件的結果當成參數傳遞給下一個中間件
// 函數列表的每個函數執行后返回的還是一個函數:action => next(action)
// 這個函數就是新的dispatch
// 最后返回函數:(...args) => action => args(action)
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
compose的源碼及其簡潔,但是很精髓,幾乎是整個中間件最出彩的地方。通過reduce把每個中間件都執行一遍,并且是通過管道式的傳輸,把每個中間件的返回結果當成參數傳遞給下一個中間件,實現了剝洋蔥式的中間件模式。這里比較難理解,新手可以先寫幾個簡單的中間件,然后再去慢慢理解為什么要這么處理,理解后就會知道這段代碼有多簡潔了。
四、手寫一個Redux
源碼解析完了,我們來簡單實現一個redux。
createStore
// 判斷值是否是對象類型
function isPlainObject(obj) {
if(!obj) {
reutrn false
}
return Object.prototype.toString.call(obj) === '[object, Object]'
}
export default createStore(reducer, enhancer) {
// 先判斷有沒有傳入中間件
// 有則之間返回
if(typeof enhancer !== 'undefined') {
// 必需是個函數
// 因為需要傳參
if(typeof enhancer !== 'function') {
return
}
return enhancer(createStore)(reducer)
}
let state = {} // 初始化state
let listeners = [] // 發布訂閱函數隊列
// 定義getState 函數
function getState() {
// 直接返回state
return state
}
// 定義dispatch 函數
function dispatch(action) {
try{
// 執行reducer, 返回state
state = reducer(state, action)
}catch(e) {
console.log('dispatch error: 'e)
}
// 訂閱
listeners.forEach(listener => listener())
// 返回action
return action
}
// 定義subscribe 函數
function subscribe(listener) {
if(!listener) {
return
}
// 必需是回掉函數
// 因為需要在dispatch里執行
if(typeof listener !== 'function') {
return
}
Listeners.push(listener)
}
// 返回對象:包含getState, dispatch, subscribe 三個方法
return {
getState,
dispatch,
subscribe
}
}
compose
function compose(...funs) {
if(!funs) {
return arg => arg
}
if(funs.length === 1) {
return funs[0]
}
// 遍歷傳入函數,返回一個新函數
return funs.reduce((a,b) => (...args) => a(b(...args)))
}
applyMiddleWare
import compose from './compose'
function applyMiddleWare(...middlewares) {
return createStore => reducer => {
// 先返回一個store
const store = createStore(reducer)
// 創建middleApi
const middleApi = {
getState: store.getState,
dispatch: (...args) => store.dispatch(...args) // 返回一個新的dispatch
}
// 注入middleApi
// 并返回函數集合
const chain = middlewares.map(middleWare => middleWare(middleApi))
// 通過compose函數,執行所有中間件,并返回一個新的dispatch
const dispatch = compose(...chain)(store.dispatch)
// 返回store對象
return {
getState: store.getState,
dispatch
}
}
}
logger中間件
function logger({getState, dispatch}) {
return function(next) {
return function(action) {
console.log('prev')
next(action)
console.log('done')
}
}
}
測試
import createStore from './myCreateStore'
import applyMiddleWare from './myApplyMiddleWare'
import logger from './logger'
// 創建reducer
function reducer(state={}, action) {
switch(action.type) {
case 'TEST':
return {
...state,
test: 'test success'
}
}
}
// 引入中間件
const middleware = applyMiddleWare(logger)
const store = createStore(reducer, middleware) // 返回{getState, dispatch}
總結
至此一個完整的redux我們就已經分析完了,個人認為中間件的compose這里是比較不好理解的點,但是只要明白中間件主要要解決的是增強dispatch函數,就可以順著這個思路去理解。接著再試著寫幾個中間件,進一步理解為什么中間件的格式需要返回嵌套的三層函數,明白了這兩個點,redux的原理也就基本能夠明白了,有問題歡迎在評論中指出。
原文鏈接:https://blog.csdn.net/grooyo/article/details/127782087
相關推薦
- 2022-09-06 關于react+antd樣式不生效問題的解決方式_React
- 2022-11-14 react使用useImperativeHandle示例詳解_React
- 2022-05-17 springboot打包為jar
- 2022-06-10 ASP.NET?Core使用EF保存數據、級聯刪除和事務使用_實用技巧
- 2022-05-17 C++?std::shared_mutex讀寫鎖的使用_C 語言
- 2021-12-19 C/C++?Qt?TabWidget?實現多窗體創建詳解_C 語言
- 2023-04-26 numpy數組切片的使用_python
- 2022-05-31 C語言中回調函數的含義與使用場景詳解_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支