網(wǎng)站首頁 編程語言 正文
一、Redux是什么
眾所周知,Redux最早運(yùn)用于React框架中,是一個全局狀態(tài)管理器。Redux解決了在開發(fā)過程中數(shù)據(jù)無限層層傳遞而引發(fā)的一系列問題,因此我們有必要來了解一下Redux到底是如何實現(xiàn)的?
二、Redux的核心思想
Redux主要分為幾個部分:dispatch、reducer、state。
我們著重看下dispatch,該方法是Redux流程的第一步,在用戶界面中通過執(zhí)行dispatch,傳入相對應(yīng)的action對象參數(shù),action是一個描述類型的對象,緊接著執(zhí)行reducer,最后整體返回一個store對象,我們來看下這部分的源碼:
// 主函數(shù)createStore
// 返回一個store對象
export default function createStore(reducer, preloadedState, enhancer) {
// 增強(qiáng)器
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
// 參數(shù)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
// 執(zhí)行傳入的reducer函數(shù)
// 返回state,給currentState賦值
currentState = currentReducer(currentState, action)
} finally {
// 一個dispatch執(zhí)行完,還原狀態(tài)
isDispatching = false
}
// 執(zhí)行訂閱函數(shù)隊列
// dispatch執(zhí)行的同時會一并執(zhí)行訂閱隊列
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.
// 默認(rèn)執(zhí)行一次dispatch,做初始化
dispatch({ type: ActionTypes.INIT })
// 返回一個store對象
return {
dispatch,
subscribe,
getState,
...
}
}
通過源碼我們可以基本清楚,通過執(zhí)行createStore方法,最終會返回一個store對象,該對象主要暴露幾個屬性,我們主要關(guān)注比較常用的:dispatch、getState、getState,看下實際用例:參考React實戰(zhàn)視頻講解:進(jìn)入學(xué)習(xí)
import createStore from 'redux'
// 創(chuàng)建一個reducer
function reducer(state={}, action) {
switch(action.type) {
case 'TEST':
return {
...state,
test: 'test success'
}
}
}
// 返回store
const store = createStore(reducer, initState={})
// 執(zhí)行dispatch
store.dispatch({
type: 'TEST'
})
const state = store.getState() // 返回 {test: 'TEST'}
三、Redux中間件原理
接下來我們來探討Redux的另一個重要組成部分—中間件。什么是Redux的中間件?Redux中間件其實是通過重寫createStore來增強(qiáng)和擴(kuò)展原來的dispatch方法,使其能夠在執(zhí)行dispatch的同時可以同步執(zhí)行其它方法,比如redux-thunk就是一個處理異步的中間件:
function createThunkMiddleware(extraArgument) {
// 中間件規(guī)定格式
// 閉包返回三層嵌套
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'// 創(chuàng)建一個reducerfunction reducer(state={}, action) { switch(action.type) { case 'TEST': return { ...state, test: 'test success' } }}// 返回store// 中間件作為applyMiddleWare的參數(shù)傳入createStoreconst store = createStore(reducer, initState={},applyMiddleWare(reduxThunk))
我們會發(fā)現(xiàn),中間件的使用方式是用applyMiddleWare把中間件作為參數(shù)傳入createStore中,那么applyMiddleWare是如何實現(xiàn)的?在這之前我們先看下createStore方法的第三個參數(shù)是什么,我們回看下createStore源碼:
export default function createStore(reducer, preloadedState, enhancer) {
...
// 增強(qiáng)器
// 第三個參數(shù)是enhancer,也就是我們傳入的applyMiddleWare
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 在這里return了enhancer結(jié)果
// 傳入了createStore,reducer,preloadedState
// 實際上是重寫了createStore
return enhancer(createStore)(reducer, preloadedState)
}
...
}
看完了enhancer的實際作用,我們可以弄清楚applyMiddleWare的實現(xiàn)原理,請看源碼:
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
}
// 執(zhí)行中間件第一層方法
// 回顧下中間的格式:({getState, dispatch}) => next => action => next(action)
// 這里會比較繞
const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 返回一個中間件的函數(shù)集合[next => action => next(action), next => action => next(action)]
// 使用compose聚合chain函數(shù)集合
// 返回新的dispatch
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
這里可能會讓人很疑惑,不大清楚的童鞋可以先看下中間件的規(guī)范寫法,這里還有一個重要的函數(shù)compose,我們來看下compose怎么處理chain函數(shù)集合的,請看源碼:
/** * 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))). */
// 傳入聚合函數(shù)集合
// 集合為:[next => action => next(action), next => action => next(action)]
// 返回一個新的函數(shù): (arg) => arg
export default function compose(...funcs) {
// 判斷如果沒有則返回一個新函數(shù)
// 可以聯(lián)想一下dispatch的定義
// function dispatch(action) {
...
return action
}
if (funcs.length === 0) {
return arg => arg
}
// 判斷如果只有一個中間件,則直接返回第一個
if (funcs.length === 1) {
return funcs[0]
}
// 這里用了reduce函數(shù)
// 把后一個的中間件的結(jié)果當(dāng)成參數(shù)傳遞給下一個中間件
// 函數(shù)列表的每個函數(shù)執(zhí)行后返回的還是一個函數(shù):action => next(action)
// 這個函數(shù)就是新的dispatch
// 最后返回函數(shù):(...args) => action => args(action)
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
compose的源碼及其簡潔,但是很精髓,幾乎是整個中間件最出彩的地方。通過reduce把每個中間件都執(zhí)行一遍,并且是通過管道式的傳輸,把每個中間件的返回結(jié)果當(dāng)成參數(shù)傳遞給下一個中間件,實現(xiàn)了剝洋蔥式的中間件模式。這里比較難理解,新手可以先寫幾個簡單的中間件,然后再去慢慢理解為什么要這么處理,理解后就會知道這段代碼有多簡潔了。
四、手寫一個Redux
源碼解析完了,我們來簡單實現(xiàn)一個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') {
// 必需是個函數(shù)
// 因為需要傳參
if(typeof enhancer !== 'function') {
return
}
return enhancer(createStore)(reducer)
}
let state = {} // 初始化state
let listeners = [] // 發(fā)布訂閱函數(shù)隊列
// 定義getState 函數(shù)
function getState() {
// 直接返回state
return state
}
// 定義dispatch 函數(shù)
function dispatch(action) {
try{
// 執(zhí)行reducer, 返回state
state = reducer(state, action)
}catch(e) {
console.log('dispatch error: 'e)
}
// 訂閱
listeners.forEach(listener => listener())
// 返回action
return action
}
// 定義subscribe 函數(shù)
function subscribe(listener) {
if(!listener) {
return
}
// 必需是回掉函數(shù)
// 因為需要在dispatch里執(zhí)行
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]
}
// 遍歷傳入函數(shù),返回一個新函數(shù)
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)
// 創(chuàng)建middleApi
const middleApi = {
getState: store.getState,
dispatch: (...args) => store.dispatch(...args) // 返回一個新的dispatch
}
// 注入middleApi
// 并返回函數(shù)集合
const chain = middlewares.map(middleWare => middleWare(middleApi))
// 通過compose函數(shù),執(zhí)行所有中間件,并返回一個新的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'
// 創(chuàng)建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}
總結(jié)
至此一個完整的redux我們就已經(jīng)分析完了,個人認(rèn)為中間件的compose這里是比較不好理解的點,但是只要明白中間件主要要解決的是增強(qiáng)dispatch函數(shù),就可以順著這個思路去理解。接著再試著寫幾個中間件,進(jìn)一步理解為什么中間件的格式需要返回嵌套的三層函數(shù),明白了這兩個點,redux的原理也就基本能夠明白了,有問題歡迎在評論中指出。
原文鏈接:https://blog.csdn.net/grooyo/article/details/127782087
相關(guān)推薦
- 2022-04-19 jQuery下實現(xiàn)等待指定元素加載完畢
- 2022-09-17 Python?seaborn數(shù)據(jù)可視化繪圖(直方圖,密度圖,散點圖)_python
- 2022-06-28 ASP.NET一次性對GridView批量更新多行數(shù)據(jù)_實用技巧
- 2023-12-20 UML類圖中各箭頭表示總結(jié)
- 2022-09-13 C++中string使用+號與int拼接方式_C 語言
- 2023-04-03 Python調(diào)試神器之PySnooper的使用教程分享_python
- 2023-02-12 JetpackCompose?Scaffold組件使用教程_Android
- 2022-01-26 iconv(): Detected an illegal character in input st
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支