电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

詳解JavaScript狀態容器Redux

瀏覽:7日期:2023-06-02 09:18:39
目錄一、Why Redux二、Redux Data flow三、Three Principles(三大原則)四、Redux源碼解析4.1、index.js4.2、createStore.js4.3、combineReducers.js4.4、bindActionCreators.js4.5、compose.js4.6、applyMiddleware.js五、從零開始實現一個簡單的Redux六、Redux Devtools七、總結一、Why Redux

在說為什么用 Redux 之前,讓我們先聊聊組件通信有哪些方式。常見的組件通信方式有以下幾種:

父子組件:props、state/callback回調來進行通信 單頁面應用:路由傳值 全局事件比如EventEmitter監聽回調傳值 react中跨層級組件數據傳遞Context(上下文)

在小型、不太復雜的應用中,一般用以上幾種組件通信方式基本就足夠了。

但隨著應用逐漸復雜,數據狀態過多(比如服務端響應數據、瀏覽器緩存數據、UI狀態值等)以及狀態可能會經常發生變化的情況下,使用以上組件通信方式會很復雜、繁瑣以及很難定位、調試相關問題。

因此狀態管理框架(如 Vuex、MobX、Redux等)就顯得十分必要了,而 Redux 就是其中使用最廣、生態最完善的。

二、Redux Data flow

在一個使用了 Redux 的 App應用里面會遵循下面四步:

第一步:通過store.dispatch(action)來觸發一個action,action就是一個描述將要發生什么的對象。如下:

{ type: ’LIKE_ARTICLE’, articleId: 42 }{ type: ’FETCH_USER_SUCCESS’, response: { id: 3, name: ’Mary’ } }{ type: ’ADD_TODO’, text: ’金融前端.’ }

第二步:Redux會調用你提供的 Reducer函數。

第三步:根 Reducer 會將多個不同的 Reducer 函數合并到單獨的狀態樹中。

第四步:Redux store會保存從根 Reducer 函數返回的完整狀態樹。

所謂一圖勝千言,下面我們結合 Redux 的數據流圖來熟悉這一過程。

詳解JavaScript狀態容器Redux

三、Three Principles(三大原則)

1、Single source of truth:單一數據源,整個應用的state被存儲在一個對象樹中,并且只存在于唯一一個store中。

2、State is read-only:state里面的狀態是只讀的,不能直接去修改state,只能通過觸發action來返回一個新的state。

3、Changes are made with pure functions:要使用純函數來修改state。

四、Redux源碼解析

Redux 源碼目前有js和ts版本,本文先介紹 js 版本的 Redux 源碼。Redux 源碼行數不多,所以對于想提高源碼閱讀能力的開發者來說,很值得前期來學習。

Redux源碼主要分為6個核心js文件和3個工具js文件,核心js文件分別為index.js、createStore.js、compose.js、combineRuducers.js、bindActionCreators.js和applyMiddleware.js文件。

接下來我們來一一學習。

4.1、index.js

index.js是入口文件,提供核心的API,如createStore、combineReducers、applyMiddleware等。

export { createStore, combineReducers, bindActionCreators, applyMiddleware, compose, __DO_NOT_USE__ActionTypes}4.2、createStore.js

createStore是 Redux 提供的API,用來生成唯一的store。store提供getState、dispatch、subscibe等方法,Redux 中的store只能通過dispatch一個action,通過action來找對應的 Reducer函數來改變。

export default function createStore(reducer, preloadedState, enhancer) {...}

從源碼中可以知道,createStore接收三個參數:Reducer、preloadedState、enhancer。

Reducer是action對應的一個可以修改store中state的純函數。

preloadedState代表之前state的初始化狀態。

enhancer是中間件通過applyMiddleware生成的一個加強函數。store中的getState方法是獲取當前應用中store中的狀態樹。

/** * Reads the state tree managed by the store. * * @returns {any} The current state tree of your application. */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的,這是唯一的一種能觸發狀態發生改變的方法。subscribe是一個監聽器,當一個action被dispatch的時候或者某個狀態發生改變的時候會被調用。

4.3、combineReducers.js

/** * Turns an object whose values are different reducer functions, into a single * reducer function. It will call every child reducer, and gather their results * into a single state object, whose keys correspond to the keys of the passed * reducer functions. */export default function combineReducers(reducers) { const reducerKeys = Object.keys(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 //判斷state是否發生改變 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } //根據是否發生改變,來決定返回新的state還是老的state return hasChanged ? nextState : state }}

從源碼可以知道,入參是 Reducers,返回一個function。combineReducers就是將所有的 Reducer合并成一個大的 Reducer 函數。核心關鍵的地方就是每次 Reducer 返回新的state的時候會和老的state進行對比,如果發生改變,則hasChanged為true,觸發頁面更新。反之,則不做處理。

4.4、bindActionCreators.js

/** * Turns an object whose values are action creators, into an object with the * same keys, but with every function wrapped into a `dispatch` call so they * may be invoked directly. This is just a convenience method, as you can call * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. */function bindActionCreator(actionCreator, dispatch) { return function() { return dispatch(actionCreator.apply(this, arguments)) }} export default function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === ’function’) { return bindActionCreator(actionCreators, dispatch) } ... ... 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}

bindActionCreator是將單個actionCreator綁定到dispatch上,bindActionCreators就是將多個actionCreators綁定到dispatch上。

bindActionCreator就是將發送actions的過程簡化,當調用這個返回的函數時就自動調用dispatch,發送對應的action。

bindActionCreators根據不同類型的actionCreators做不同的處理,actionCreators是函數就返回函數,是對象就返回一個對象。主要是將actions轉化為dispatch(action)格式,方便進行actions的分離,并且使代碼更加簡潔。

4.5、compose.js

/** * 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))). */ export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args)))}

compose是函數式變成里面非常重要的一個概念,在介紹compose之前,先來認識下什么是 Reduce?官方文檔這么定義reduce:reduce()方法對累加器和數組中的每個元素(從左到右)應用到一個函數,簡化為某個值。compose是柯里化函數,借助于Reduce來實現,將多個函數合并到一個函數返回,主要是在middleware中被使用。

4.6、applyMiddleware.js

/** * Creates a store enhancer that applies middleware to the dispatch method * of the Redux store. This is handy for a variety of tasks, such as expressing * asynchronous actions in a concise manner, or logging every action payload. */export default function applyMiddleware(...middlewares) { return createStore => (...args) => { const store = createStore(...args) ... ... return { ...store, dispatch } }}

applyMiddleware.js文件提供了middleware中間件重要的API,middleware中間件主要用來對store.dispatch進行重寫,來完善和擴展dispatch功能。

那為什么需要中間件呢?

首先得從Reducer說起,之前 Redux三大原則里面提到了reducer必須是純函數,下面給出純函數的定義:

對于同一參數,返回同一結果 結果完全取決于傳入的參數 不產生任何副作用

至于為什么reducer必須是純函數,可以從以下幾點說起?

因為 Redux 是一個可預測的狀態管理器,純函數更便于 Redux進行調試,能更方便的跟蹤定位到問題,提高開發效率。 Redux 只通過比較新舊對象的地址來比較兩個對象是否相同,也就是通過淺比較。如果在 Reducer 內部直接修改舊的state的屬性值,新舊兩個對象都指向同一個對象,如果還是通過淺比較,則會導致 Redux 認為沒有發生改變。但要是通過深比較,會十分耗費性能。最佳的辦法是 Redux返回一個新對象,新舊對象通過淺比較,這也是 Reducer是純函數的重要原因。

Reducer是純函數,但是在應用中還是會需要處理記錄日志/異常、以及異步處理等操作,那該如何解決這些問題呢?

這個問題的答案就是中間件。可以通過中間件增強dispatch的功能,示例(記錄日志和異常)如下:

const store = createStore(reducer);const next = store.dispatch; // 重寫store.dispatchstore.dispatch = (action) => { try {console.log(’action:’, action);console.log(’current state:’, store.getState());next(action);console.log(’next state’, store.getState()); } catch (error){console.error(’msg:’, error); }}五、從零開始實現一個簡單的Redux

既然是要從零開始實現一個Redux(簡易計數器),那么在此之前我們先忘記之前提到的store、Reducer、dispatch等各種概念,只需牢記Redux是一個狀態管理器。

首先我們來看下面的代碼:

let state = { count : 1}//修改之前console.log (state.count);//修改count的值為2state.count = 2;//修改之后console.log (state.count);

我們定義了一個有count字段的state對象,同時能輸出修改之前和修改之后的count值。但此時我們會發現一個問題?就是其它如果引用了count的地方是不知道count已經發生修改的,因此我們需要通過訂閱-發布模式來監聽,并通知到其它引用到count的地方。因此我們進一步優化代碼如下:

let state = { count: 1};//訂閱function subscribe (listener) { listeners.push(listener);}function changeState(count) { state.count = count; for (let i = 0; i < listeners.length; i++) {const listener = listeners[i];listener();//監聽 }}

此時我們對count進行修改,所有的listeners都會收到通知,并且能做出相應的處理。但是目前還會存在其它問題?比如說目前state只含有一個count字段,如果要是有多個字段是否處理方式一致。同時還需要考慮到公共代碼需要進一步封裝,接下來我們再進一步優化:

const createStore = function (initState) { let state = initState; //訂閱 function subscribe (listener) {listeners.push(listener); } function changeState (count) {state.count = count;for (let i = 0; i < listeners.length; i++) { const listener = listeners[i]; listener();//通知} } function getState () {return state; } return {subscribe,changeState,getState }}

我們可以從代碼看出,最終我們提供了三個API,是不是與之前Redux源碼中的核心入口文件index.js比較類似。但是到這里還沒有實現Redux,我們需要支持添加多個字段到state里面,并且要實現Redux計數器。

let initState = { counter: {count : 0 }, info: {name: ’’,description: ’’ }}let store = createStore(initState);//輸出countstore.subscribe(()=>{ let state = store.getState(); console.log(state.counter.count);});//輸出infostore.subscribe(()=>{ let state = store.getState(); console.log(`${state.info.name}:${state.info.description}`);});

通過測試,我們發現目前已經支持了state里面存多個屬性字段,接下來我們把之前changeState改造一下,讓它能支持自增和自減。

//自增store.changeState({ count: store.getState().count + 1});//自減store.changeState({ count: store.getState().count - 1});//隨便改成什么store.changeState({ count: 金融});

我們發現可以通過changeState自增、自減或者隨便改,但這其實不是我們所需要的。我們需要對修改count做約束,因為我們在實現一個計數器,肯定是只希望能進行加減操作的。所以我們接下來對changeState做約束,約定一個plan方法,根據type來做不同的處理。

function plan (state, action) => { switch (action.type) { case ’INCREMENT’: return {...state,count: state.count + 1 } case ’DECREMENT’: return {...state,count: state.count - 1 } default: return state }}let store = createStore(plan, initState);//自增store.changeState({ type: ’INCREMENT’});//自減store.changeState({ type: ’DECREMENT’});

我們在代碼中已經對不同type做了不同處理,這個時候我們發現再也不能隨便對state中的count進行修改了,我們已經成功對changeState做了約束。我們把plan方法做為createStore的入參,在修改state的時候按照plan方法來執行。到這里,恭喜大家,我們已經用Redux實現了一個簡單計數器了。

這就實現了 Redux?這怎么和源碼不一樣啊

然后我們再把plan換成reducer,把changeState換成dispatch就會發現,這就是Redux源碼所實現的基礎功能,現在再回過頭看Redux的數據流圖是不是更加清晰了。

詳解JavaScript狀態容器Redux

六、Redux Devtools

Redux devtools是Redux的調試工具,可以在Chrome上安裝對應的插件。對于接入了Redux的應用,通過 Redux devtools可以很方便看到每次請求之后所發生的改變,方便開發同學知道每次操作后的前因后果,大大提升開發調試效率。

詳解JavaScript狀態容器Redux

如上圖所示就是 Redux devtools的可視化界面,左邊操作界面就是當前頁面渲染過程中執行的action,右側操作界面是State存儲的數據,從State切換到action面板,可以查看action對應的 Reducer參數。切換到Diff面板,可以查看前后兩次操作發生變化的屬性值。

七、總結

Redux 是一款優秀的狀態管理器,源碼短小精悍,社區生態也十分成熟。如常用的react-redux、dva都是對 Redux 的封裝,目前在大型應用中被廣泛使用。這里推薦通過Redux官網以及源碼來學習它核心的思想,進而提升閱讀源碼的能力。

以上就是詳解JavaScript狀態容器Redux的詳細內容,更多關于JavaScript狀態容器Redux的資料請關注好吧啦網其它相關文章!

標簽: JavaScript
相關文章:
主站蜘蛛池模板: 氧化锆纤维_1800度高温退火炉_1800度高温烧结炉-南京理工宇龙新材料股份有限公司 | 广东佛电电器有限公司|防雷开关|故障电弧断路器|智能量测断路器 广东西屋电气有限公司-广东西屋电气有限公司 | 电磁辐射仪-电磁辐射检测仪-pm2.5检测仪-多功能射线检测仪-上海何亦仪器仪表有限公司 | 欧景装饰设计工程有限公司-无锡欧景装饰官网 | 北京环球北美考试院【官方网站】|北京托福培训班|北京托福培训 | 桁架机器人_桁架机械手_上下料机械手_数控车床机械手-苏州清智科技装备制造有限公司 | 胶辊硫化罐_胶鞋硫化罐_硫化罐厂家-山东鑫泰鑫智能装备有限公司 意大利Frascold/富士豪压缩机_富士豪半封闭压缩机_富士豪活塞压缩机_富士豪螺杆压缩机 | 知网论文检测系统入口_论文查重免费查重_中国知网论文查询_学术不端检测系统 | 武汉森源蓝天环境科技工程有限公司-为环境污染治理提供协同解决方案 | 爆破器材运输车|烟花爆竹运输车|1-9类危险品厢式运输车|湖北江南专用特种汽车有限公司 | 广州工业氧气-工业氩气-工业氮气-二氧化碳-广州市番禺区得力气体经营部 | 天津散热器_天津暖气片_天津安尼威尔散热器制造有限公司 | IP检测-检测您的IP质量| 机床主轴维修|刀塔维修|C轴维修-常州翔高精密机械有限公司 | 化工ERP软件_化工新材料ERP系统_化工新材料MES软件_MES系统-广东顺景软件科技有限公司 | 斗式提升机,斗式提升机厂家-淄博宏建机械有限公司 | 上海软件开发-上海软件公司-软件外包-企业软件定制开发公司-咏熠科技 | 精密机械零件加工_CNC加工_精密加工_数控车床加工_精密机械加工_机械零部件加工厂 | 低粘度纤维素|混凝土灌浆料|有机硅憎水粉|聚羧酸减水剂-南京斯泰宝 | 冷油器-冷油器换管改造-连云港灵动列管式冷油器生产厂家 | 广西绿桂涂料--承接隔热涂料、隔音涂料、真石漆、多彩仿石漆等涂料工程双包施工 | 北京亦庄厂房出租_经开区产业园招商信息平台 | 驾驶人在线_专业学车门户网站 | POM塑料_PBT材料「进口」聚甲醛POM杜邦原料、加纤PBT塑料报价格找利隆塑料 | 北京晚会活动策划|北京节目录制后期剪辑|北京演播厅出租租赁-北京龙视星光文化传媒有限公司 | 氧化锆纤维_1800度高温退火炉_1800度高温烧结炉-南京理工宇龙新材料股份有限公司 | 送料机_高速冲床送料机_NC伺服滚轮送料机厂家-东莞市久谐自动化设备有限公司 | 生产加气砖设备厂家很多,杜甫机械加气砖设备价格公道 | 乐泰胶水_loctite_乐泰胶_汉高乐泰授权(中国)总代理-鑫华良供应链 | 量子管通环-自清洗过滤器-全自动反冲洗过滤器-沼河浸过滤器 | 炉门刀边腹板,焦化设备配件,焦化焦炉设备_沧州瑞创机械制造有限公司 | 瑞典Blueair空气净化器租赁服务中心-专注新装修办公室除醛去异味服务! | 烟台游艇培训,威海游艇培训-烟台市邮轮游艇行业协会 | 散热器厂家_暖气片_米德尔顿散热器 | 量子管通环-自清洗过滤器-全自动反冲洗过滤器-北京罗伦过滤技术集团有限公司 | 酒吧霸屏软件_酒吧霸屏系统,酒吧微上墙,夜场霸屏软件,酒吧点歌软件,酒吧互动游戏,酒吧大屏幕软件系统下载 | 在线钠离子分析仪-硅酸根离子浓度测定仪-油液水分测定仪价格-北京时代新维测控设备有限公司 | 储气罐,真空罐,缓冲罐,隔膜气压罐厂家批发价格,空压机储气罐规格型号-上海申容压力容器集团有限公司 | 莱州网络公司|莱州网站建设|莱州网站优化|莱州阿里巴巴-莱州唯佳网络科技有限公司 | 工业雾炮机_超细雾炮_远程抑尘射雾器-世纪润德环保设备 | led冷热冲击试验箱_LED高低温冲击试验箱_老化试验箱-爱佩百科 |