網(wǎng)站首頁 編程語言 正文
概述
- 一個狀態(tài)管理工具
- Store:保存數(shù)據(jù)的地方,你可以把它看成一個容器,整個應(yīng)用只能有一個 Store。
- State:包含所有數(shù)據(jù),如果想得到某個時點的數(shù)據(jù),就要對 Store 生成快照,這種時點的數(shù)據(jù)集合,就叫做 State。
- Action:Action 就是 View 發(fā)出的通知,表示 State 應(yīng)該要發(fā)生變化了。
- Action Creator:View 要發(fā)送多少種消息,就會有多少種 Action。如果都手寫,會很麻煩,所以我們定義一個函數(shù)來生成 Action,這個函數(shù)就叫 Action Creator。
- Reducer:Store 收到 Action 以后,必須給出一個新的 State。這種 State 的計算過程就叫做 Reducer。Reducer 是一個函數(shù),它接受 Action 和當(dāng)前 State 作為參數(shù),返回一個新的 State。
- dispatch:是 View 發(fā)出 Action 的唯一方法。
整個工作流程:
- 首先,用戶(通過 View)發(fā)出 Action,發(fā)出方式就用到了 dispatch 方法。
- 然后,Store 自動調(diào)用 Reducer,并且傳入兩個參數(shù):當(dāng)前 State 和收到的 Action,Reducer 會返回新的 State
- State 一旦有變化,Store 就會調(diào)用監(jiān)聽函數(shù),來更新 View。
三大原則
1.單一數(shù)據(jù)源(Store)?整個應(yīng)用的State被存放在一棵Object tree中,并且這個Object tree只存在唯一一個Store中;
2.State是只讀的 唯一改變 State 的方法就是觸發(fā) Action,Action 是一個用于描述已發(fā)生事件的普通對象。?確保了所有的修改都能被集中化處理。
3.通過純函數(shù)Reducer來修改Store,?Reducer 只是一些純函數(shù),它接收先前的 State 和 Action,并返回新的 State。 即reducer(state, action) => new state
createStore創(chuàng)建store
- createStore 方法接受 3 個參數(shù)參數(shù) (reducer, [preloadedState], enhancer);
返回 store,store 上掛載著 dispatch、getState、subscribe、replaceReducer 等方法 - 第二個參數(shù)是 preloadedState,它是 state 的初始值,實際上他并不僅僅是扮演著一個 initialState 的角色,如果我們第二個參數(shù)是函數(shù)類型,createStore 會認(rèn)為傳入了一個 enhancer,如果我們傳入了一個 enhancer,createStore 會返回 enhancer(createStore)(reducer, preloadedState)的調(diào)用結(jié)果,這是常見高階函數(shù)的調(diào)用方式。
- enhancer 接受 createStore 作為參數(shù),對 createStore 的能力進(jìn)行增強,并返回增強后的 createStore。然后再將 reducer 和 preloadedState 作為參數(shù)傳給增強后的 createStore,最終得到生成的 store。
export default function createStore(reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
// 第二個參數(shù)是一個函數(shù),沒有第三個參數(shù)的情況
enhancer = preloadedState;
preloadedState = undefined;
}
// 如果第三個參數(shù)是函數(shù)走下面的邏輯,返回一個新的createStore
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
// enhancer 不是函數(shù)就報錯
throw new Error('Expected the enhancer to be a function.');
}
// enhancer就是高階函數(shù),強化了本身這個createStore的函數(shù),拿到增強后的createStore函數(shù)去處理
// applyMiddleware這個函數(shù)還會涉及到這個
return enhancer(createStore)(reducer, preloadedState);
}
if (typeof reducer !== 'function') {
// reducer不是函數(shù)報錯
throw new Error('Expected the reducer to be a function.');
}
// 其他代碼省略
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable,
};
}
applyMiddleware 應(yīng)用中間件
- 返回一個函數(shù) enhancer;
- 右邊中間件的執(zhí)行時機由左邊的中間件決定,這是因為 next 的方法的調(diào)用時機由右邊控制
- dispatch 的處理使用了閉包,這樣保證在中間件中調(diào)用 dispatch 也是用的最終的 dispatch,也是同一個 dispatch;
- 寫中間件的時候如果要調(diào)用 dispatch,一定要有跳出條件,防止死循環(huán)
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),
};
const chain = middlewares.map((middleware) => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch,
};
};
}
// 其實就是修改了dispatch
let store = applyMiddleware(middleware1,middleware2)(createStore)(rootReducer);
combineReducers 合并多個reducer
從執(zhí)行結(jié)果看,這時候 state 已經(jīng)變成了一個以這些 reducer 為 key 的對象;
reducer 也變成了一個合并的 reducer;
遍歷執(zhí)行所有的 reducer 的時候把 action 傳進(jìn)去,返回新的 state;
export default function combineReducers(reducers) {
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);
/* 返回一個整合后的reducers */
return function combination(state = {}, action) {
let hasChanged = false;
const nextState = {};
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);
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action);
throw new Error(errorMessage);
}
nextState[key] = nextStateForKey;
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
}
return hasChanged ? nextState : state;
};
}
dispatch
- 默認(rèn)的 action 只能是普通對象,除非使用了第三方中間件,比如 redux-thunk
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.',
);
}
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;
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;
}
中間件
- Redux 中間件在發(fā)起一個 action 至 action 到達(dá) reducer 的之間提供了一個第三方的擴(kuò)展。本質(zhì)上通過插件的形式,將原本的 action->redux 的流程改變?yōu)?action->middleware1->middleware2-> ... ->reducer,通過改變數(shù)據(jù)流,從而實現(xiàn)例如異步 Action、日志輸入的功能。
- Redux 中間件范式
- 一個中間件接收 store 作為參數(shù),會返回一個函數(shù)
- 返回的這個函數(shù)接收老的 dispatch 函數(shù)作為參數(shù)(一般用 next 作為形參),會返回一個新的函數(shù)
- 返回的新函數(shù)就是新的 dispatch 函數(shù),這個函數(shù)里面可以拿到外面兩層傳進(jìn)來的 store 和老 dispatch 函數(shù)
function logger(store) {
return function (next) {
return function (action) { // 新的 dispatch 函數(shù)
console.group(action.type);
console.info('dispatching', action);
let result = next(action);
console.log('next state', store.getState());
console.groupEnd();
return result;
};
};
}
中間件的調(diào)用順序
- 首先調(diào)用順序和書寫順序是一致的,但是這里面的洋蔥模型包含了兩次順序,從洋蔥出來的順序是和書寫順序相反
import React from 'react';
import { createStore, applyMiddleware } from 'redux';
function createLogger({ getState, dispatch }) {
return (next) => (action) => {
const prevState = getState();
console.log('createLogger1');
const returnValue = next(action);
const nextState = getState();
const actionType = String(action.type);
const message = `action ${actionType}`;
console.log(`%c prev state`, `color: #9E9E9E`, prevState);
console.log(`%c action`, `color: #03A9F4`, action);
console.log(`%c next state`, `color: #4CAF50`, nextState);
return returnValue;
};
}
function createLogger2({ getState }) {
return (next) => (action) => {
const console = window.console;
const prevState = getState();
console.log('createLogger2');
const returnValue = next(action);
const nextState = getState();
const actionType = String(action.type);
const message = `action ${actionType}`;
console.log(`%c prev state2`, `color: #9E9E9E`, prevState);
console.log(`%c action2`, `color: #03A9F4`, action);
console.log(`%c next state2`, `color: #4CAF50`, nextState);
return returnValue;
};
}
const reducer = function (state = { number: 0 }, action) {
switch (action.type) {
case 'add':
return {
number: state.number + action.number,
};
default:
return state;
}
};
const store = createStore(
reducer,
applyMiddleware(createLogger, createLogger2),
);
store.subscribe(function () {
console.log(1111);
});
const { dispatch } = store;
const App = () => {
const handler = () => {
dispatch({ type: 'add', number: 10 });
};
return (
<div>
<button onClick={handler}>觸發(fā)redux</button>
</div>
);
};
export default App;
store
store 的屬性如下:
dispatch: ? dispatch(action)
getState: ? getState()
replaceReducer: ? replaceReducer(nextReducer)
subscribe: ? subscribe(listener)
redux 數(shù)據(jù)流
Redux 的數(shù)據(jù)流是這樣的:
界面 => action => reducer => store => react => virtual dom => 界面
bindActionCreators
將action對象轉(zhuǎn)為一個帶dispatch的方法
比如connect接收的mapDispatchToProps 是對象,會使用 bindActionCreators 處理; 接收 actionCreator 和 dispatch,返回一個函數(shù);
function bindActionCreator(actionCreator, dispatch) {
// 返回一個函數(shù)
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
const mapDispatchToProps = { // actionCreators 這是個集合,
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
};
}
轉(zhuǎn)換為:
const mapDispatchToProps = { // actionCreators 這是個集合,
onClick:function(filter) {
return dispatch({ // dispatch 是閉包中的方法
type: 'SET_VISIBILITY_FILTER',
filter: filter
})
}
}
compose
函數(shù)套函數(shù),compose(...chain)(store.dispatch)結(jié)果返回一個加強了的 dispatch;
這點和koa比較相似,這個 dispatch 在執(zhí)行的時候會調(diào)用中間件。
function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
// 每一次reduce迭代都會返回一個加強版的dispatch
return funcs.reduce(
(a, b) =>
(...args) =>
a(b(...args)),
);
}
加強版 dispatch(一個方法,接收 action 參數(shù)),在中間件中用 next 表示,執(zhí)行 next 之后,會形成一個鏈條。
enhancer
- enhancer,翻譯過來就是 store 加強器,比如 applymiddleware 的返回值就是一個 enhaner。store 加強器可以重新構(gòu)建一個更強大的 store,來替換之前的基礎(chǔ)版的 store,讓你的程序可以增加很多別的功能,比如 appllymiddleware 可以給你的 redux 增加中間件,使之可以擁有異步功能,日志功能等!
// 以createStore為參數(shù)
(createStore) =>
(...args) => {};
使用 redux 常遇見的問題
- 樣板代碼過多 增加一個 action 往往需要同時定義相應(yīng)的 actionType 然后再寫相關(guān)的 reducer。例如當(dāng)添加一個異步加載事件時,需要同時定義加載中、加載失敗以及加載完成三個 actionType,需要一個相對應(yīng)的 reducer 通過 switch 分支來處理對應(yīng)的 actionType,冗余代碼過多;
- 目前已經(jīng)存在著非常多的解決方案,比如dva redux-tookit等。
- 更新效率問題:由于使用不可變數(shù)據(jù)模式,每次更新 state 都需要拷貝一份完整的 state 造成了內(nèi)存的浪費以及性能的損耗。
- 其實 redux 以及 react-redux 中內(nèi)部已做優(yōu)化,開發(fā)的時候使用 shouldComponentUpdate 等優(yōu)化方法也可以應(yīng)用,也可以用不可變數(shù)據(jù)結(jié)構(gòu)如 immutable、Immr 等來解決拷貝開銷問題。
原文鏈接:https://www.cnblogs.com/walkermag/p/16930528.html
相關(guān)推薦
- 2022-07-14 python求解三角形第三邊長實例_python
- 2022-05-06 如何利用Go語言實現(xiàn)LRU?Cache_Golang
- 2022-08-10 pandas數(shù)據(jù)清洗實現(xiàn)刪除的項目實踐_python
- 2023-01-23 Kotlin泛型的型變之路演變示例詳解_Android
- 2022-08-30 C語言遞歸函數(shù)與漢諾塔問題簡明理解_C 語言
- 2022-10-12 詳解C++異常處理機制示例介紹_C 語言
- 2023-07-30 使用Elementui元素動態(tài)增減表單組件
- 2022-08-14 在WPF中使用多線程更新UI_C#教程
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 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)程分支