摘要:是一個(gè)程序架構(gòu),源于提出的一種架構(gòu),然而,它不僅可以應(yīng)用于,還可以應(yīng)用于其他任何框架中。有以下職責(zé)維持應(yīng)用的提供方法獲取提供方法更新通過(guò)注冊(cè)監(jiān)聽器通過(guò)返回的函數(shù)注銷監(jiān)聽器。同時(shí),的返回值實(shí)際上是一個(gè)函數(shù)可以解除監(jiān)聽。
Redux是一個(gè)程序架構(gòu),源于Flux(Facebook提出的一種架構(gòu)),然而,它不僅可以應(yīng)用于React,還可以應(yīng)用于其他任何框架中。值得一提的是,Redux的源代碼很少,但是他的邏輯拆分和函數(shù)式編程的設(shè)計(jì)思想是非常值得學(xué)習(xí)的。
1. 解決的問(wèn)題當(dāng)一個(gè)JavaScript單頁(yè)應(yīng)用變得越來(lái)越復(fù)雜時(shí),我們要處理的數(shù)據(jù)(model)也變得越來(lái)越龐大,隨之而來(lái)的是每個(gè)數(shù)據(jù)的狀態(tài)(state)會(huì)變得難以維護(hù)。當(dāng)一個(gè)model改變時(shí),我們可能要手動(dòng)處理由此引發(fā)的其他model的變化,更糟糕的是,其他model變化可能又會(huì)引起另一些model的變化,這樣產(chǎn)生連鎖反應(yīng)。最后我們很容易就會(huì)不記得model在什么時(shí)候以及如何發(fā)生改變。這正是Redux可以解決的最大痛點(diǎn)之一—以統(tǒng)一的方式管理數(shù)據(jù)狀態(tài)。
統(tǒng)一的數(shù)據(jù)狀態(tài)管理體現(xiàn)為以下2個(gè)方面:
組件間的數(shù)據(jù)通信
UI和數(shù)據(jù)處理邏輯分離
2.三大原則
單一數(shù)據(jù)源
整個(gè)應(yīng)用的 state 被儲(chǔ)存在一棵 object tree 中,并且這個(gè) object tree 只存在于唯一一個(gè) store 中。
State是只讀的
唯一改變 state 的方法就是觸發(fā) action,action 是一個(gè)用于描述已發(fā)生事件的普通對(duì)象。
Reducer必須是純函數(shù)
3.基本概念
Action
Action 是把數(shù)據(jù)從應(yīng)用傳到 store 的有效載荷。它是 store 數(shù)據(jù)的唯一來(lái)源。直白的說(shuō),action就是一種消息類型,他告訴Redux是時(shí)候該做什么了,并帶著相應(yīng)的數(shù)據(jù)傳到Redux內(nèi)部(即接下來(lái)介紹的Reducer)。
Action就是一個(gè)簡(jiǎn)單的對(duì)象,其中必須要有一個(gè)type屬性,用來(lái)標(biāo)志動(dòng)作類型(reducer以此判斷要執(zhí)行的邏輯),其他屬性用戶可以自定義,但要盡量簡(jiǎn)單。如:
{type: SET_ALL,index: 5}
Action Creator
Action Creator是一個(gè)用來(lái)自動(dòng)生成Action的函數(shù),這也是Redux函數(shù)式編程的一種體現(xiàn)。通常,我們會(huì)按照業(yè)務(wù)邏輯或組件把相關(guān)的Action Creator放在一個(gè)文件中,形式如下:
export const SHOW_SLIDER = "SHOW_SLIDER"; export const RIGHT_SLIDER = "RIGHT_SLIDER"; export const showSliderAction = (index) => { return { type: SHOW_SLIDER, index }; } export const rightSliderAction = (length) => { return { type: RIGHT_SLIDER, length }; }
其中showSliderAction和rightSliderAction就是Action Creator。
由于Action Creator的形式大體相同,我們還可以創(chuàng)建一個(gè)用來(lái)生成Action Creator的函數(shù)以進(jìn)一步簡(jiǎn)化。
export const makeCreateAction = (type, ...keys) => { return (...data) => { let action = {type}; keys.forEach((v,i) => action[v] = data[i]); return action; } } export const showSliderAction = makeCreateAction(SHOW_SLIDER, index); export const rightSliderAction = makeCreateAction(RIGHT_SLIDER, length);
Reducer
Reducer 指定了應(yīng)用狀態(tài)的變化如何響應(yīng) actions 并發(fā)送到 store 的,action只是告訴Redux該干什么了,并沒(méi)有告訴他怎么干,而reducer就是根據(jù)action處理state如何改變的邏輯。
Reducer必須是一個(gè)純函數(shù)(原因稍后解釋),他根據(jù)action處理state的更新,如果沒(méi)有更新或遇到未知action,則返回舊state;否則返回一個(gè)新state對(duì)象。注意:不能修改舊state,必須先拷貝一份state,再進(jìn)行修改,也可以使用Object.assign函數(shù)生成新的state。另外,state參數(shù)需先進(jìn)行初始化。實(shí)例代碼如下:
//初始狀態(tài) let initialState = {hiddenClass: "g-hidden",currentIndex:0}; let sliderReducer = function (state = initialState, action) { switch(action.type){ case sliderAction.SHOW_SLIDER: return {hiddenClass: "",currentIndex:action.index}; case sliderAction.RIGHT_SLIDER: if(state.currentIndex == action.length-1){ return Object.assign({}, state, {currentIndex:0}); }else{ return Object.assign({}, state, {currentIndex:Number.parseInt(state.currentIndex)+1}); } default: return state; } } export default sliderReducer;
使用純函數(shù)的原因:
首先,純函數(shù)的特點(diǎn)是: ?函數(shù)的返回結(jié)果只依賴于它的參數(shù)。 ?函數(shù)執(zhí)行過(guò)程里面沒(méi)有副作用。(不會(huì)對(duì)外界產(chǎn)生影響)
如果不使用純函數(shù),即直接更改state值會(huì)怎么樣呢?
… … case sliderAction.RIGHT_SLIDER: if(state.currentIndex == action.length-1){ state.currentIndex = 0; return state; } … …
之后會(huì)發(fā)現(xiàn),無(wú)論state如何變化,UI都不會(huì)更新。以下是Redux的部分源碼:
通過(guò)查看Redux源碼得知,新舊state的比較只是對(duì)引用地址的比較,如果reducer只是返回舊state(即previousStateForKey)的更新,新state(nextStateForKey)實(shí)際上和舊state引用的都是同一塊內(nèi)存地址,所以無(wú)論如何更改,新舊state始終保持相同。這就是為什么reducer必須是純函數(shù)的原因。
Reducer拆分與合并:
Reducer 函數(shù)負(fù)責(zé)生成 State。由于整個(gè)應(yīng)用只有一個(gè) State 對(duì)象,包含所有數(shù)據(jù),對(duì)于大型應(yīng)用來(lái)說(shuō),這個(gè) State 必然十分龐大,導(dǎo)致 Reducer 函數(shù)也十分龐大。所以需要先對(duì)reducer進(jìn)行拆分,拆分的原則可以按業(yè)務(wù)邏輯進(jìn)行劃分,如果是react的話,可以直接和react的組件相對(duì)應(yīng)進(jìn)行劃分。
劃分好之后,可以用Redux提供的combineReducers方法進(jìn)行合并,十分方便。
import { combineReducers } from "redux"; import photomainReducer from "./photomainReducer"; import sortReducer from "./sortReducer"; import sliderReducer from "./photoSliderReducer"; export default combineReducers({ photomainReducer, sortReducer, sliderReducer });
Store
Store 是把Action、Reducer聯(lián)系到一起的對(duì)象。Store 有以下職責(zé):
1.維持應(yīng)用的 state; 2.提供 getState() 方法獲取 state; 3.提供 dispatch(action) 方法更新 state; 4.通過(guò) subscribe(listener) 注冊(cè)監(jiān)聽器; 5.通過(guò) subscribe(listener) 返回的函數(shù)注銷監(jiān)聽器。
(1) 創(chuàng)建store
Redux應(yīng)用應(yīng)該只有一個(gè)store,他提供了創(chuàng)建store的API—createStore(reducer, initState)。第一個(gè)參數(shù)為一個(gè)reducer,可以接受通過(guò)combineReducers合并后的reducer,第二個(gè)是可選參數(shù),意思是可以設(shè)置應(yīng)用的初始state。
const store = createStore( indexPhotomainReducer, ); export default store;
(2)State
應(yīng)用的state也應(yīng)該只有一個(gè),里面包含了所有的應(yīng)用數(shù)據(jù)。當(dāng)需要獲取state的時(shí)候,需要使用store.getState()方法。
(3)store.dispatch(action)
UI更新state 的唯一途徑,通過(guò)dispatch方法發(fā)起action,喚起對(duì)應(yīng)的reducer更新state。
(4)store. subscribe(listener)
通過(guò)此方法可以設(shè)置監(jiān)聽函數(shù),一旦state發(fā)生變化,就會(huì)立即調(diào)用監(jiān)聽函數(shù)(listener)。同時(shí),subscribe的返回值(實(shí)際上是一個(gè)unsubscribe函數(shù))可以解除監(jiān)聽。如:
// 每次 state 更新時(shí),打印日志 // 注意 subscribe() 返回一個(gè)函數(shù)用來(lái)注銷監(jiān)聽器 const unsubscribe = store.subscribe(() => console.log(store.getState()) ) // 停止監(jiān)聽 state 更新 unsubscribe();4.數(shù)據(jù)流
(1)首先,用戶發(fā)出 Action(如click事件)。
store.dispatch(SHOW_SLIDER)
(2) Store 自動(dòng)調(diào)用 Reducer,并且傳入兩個(gè)參數(shù):當(dāng)前 State 和收到的 Action。 Reducer根據(jù)Action的type調(diào)用相應(yīng)的分支, 返回新的 State 。
let initialState = {hiddenClass: "g-hidden",currentIndex:0}; let sliderReducer = function (state = initialState, action) { switch(action.type){ case sliderAction.SHOW_SLIDER: return {hiddenClass: "",currentIndex:action.index}; default: return state; } }
(3) State 一旦有變化,Store 就會(huì)調(diào)用監(jiān)聽函數(shù)。
store.subscribe(listener);
(4) listener可以通過(guò)store.getState()得到當(dāng)前狀態(tài)。如果使用的是 React,這時(shí)可以觸發(fā)重新渲染 View。
function listerner() { let newState = store.getState(); component.setState(newState); }
以上為Redux的數(shù)據(jù)流動(dòng)過(guò)程。
本篇到此告一段落,下一篇介紹Redux的異步實(shí)現(xiàn)。
參考Redux 中文文檔
Redux 入門教程-阮一峰
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/94840.html
摘要:異步實(shí)現(xiàn)設(shè)計(jì)需要增加三種通知異步請(qǐng)求發(fā)起的異步請(qǐng)求成功的異步請(qǐng)求失敗的示例代碼如下返回參數(shù)完全可以自定義。這種分別在請(qǐng)求開始前,請(qǐng)求成功后,請(qǐng)求失敗后發(fā)送。表示數(shù)據(jù)的有效性,他的作用是在異步請(qǐng)求發(fā)送失敗后,告訴當(dāng)前的數(shù)據(jù)是過(guò)時(shí)的數(shù)據(jù)。 說(shuō)明:對(duì)Redux不了解的同學(xué)可先看看這篇文章Redux技術(shù)架構(gòu)簡(jiǎn)介(一) 前言 這里說(shuō)的Redux異步實(shí)現(xiàn),是專指Redux中的異步Action實(shí)現(xiàn),...
摘要:應(yīng)用這說(shuō)明并不是單指設(shè)計(jì)給用的,它是獨(dú)立的一個(gè)函數(shù)庫(kù),可通用于各種應(yīng)用。在數(shù)據(jù)流的最后,要觸發(fā)最上層組件的,然后進(jìn)行整體的重新渲染工作。單純?cè)诘膶?duì)象上是沒(méi)有辦法使用,要靠額外的函數(shù)庫(kù)才能這樣作,這是一定要使用類似像這種函數(shù)庫(kù)的主要原因。 Redux的官網(wǎng)中用一句話來(lái)說(shuō)明Redux是什么: Redux是針對(duì)JavaScript應(yīng)用的可預(yù)測(cè)狀態(tài)容器 這句話雖然簡(jiǎn)短,其實(shí)是有幾個(gè)涵義的: ...
摘要:展示組件與容器組件的綁定庫(kù)的基本開發(fā)思想是展示組件與容器組件相分離。技術(shù)上講,容器組件就是使用從樹中讀取部分?jǐn)?shù)據(jù),并通過(guò)來(lái)把這些數(shù)據(jù)提供給要渲染的組件。 說(shuō)明:閱讀本篇文章需要對(duì)Redux有一定的了解,對(duì)Redux不了解的同學(xué)可先看看這篇文章Redux技術(shù)架構(gòu)簡(jiǎn)介(一) 1. React中引入react-redux 為了讓Redux和React更好的配合,F(xiàn)acebook專門開發(fā)了一個(gè)...
摘要:另外,內(nèi)置的函數(shù)在經(jīng)過(guò)一系列校驗(yàn)后,觸發(fā),之后被更改,之后依次調(diào)用監(jiān)聽,完成整個(gè)狀態(tài)樹的更新。總而言之,遵守這套規(guī)范并不是強(qiáng)制性的,但是項(xiàng)目一旦稍微復(fù)雜一些,這樣做的好處就可以充分彰顯出來(lái)。 這一篇是接上一篇react進(jìn)階漫談的第二篇,這一篇主要分析redux的思想和應(yīng)用,同樣參考了網(wǎng)絡(luò)上的大量資料,但代碼同樣都是自己嘗試實(shí)踐所得,在這里分享出來(lái),僅供一起學(xué)習(xí)(上一篇地址:個(gè)人博客/s...
摘要:另外,內(nèi)置的函數(shù)在經(jīng)過(guò)一系列校驗(yàn)后,觸發(fā),之后被更改,之后依次調(diào)用監(jiān)聽,完成整個(gè)狀態(tài)樹的更新。總而言之,遵守這套規(guī)范并不是強(qiáng)制性的,但是項(xiàng)目一旦稍微復(fù)雜一些,這樣做的好處就可以充分彰顯出來(lái)。 這一篇是接上一篇react進(jìn)階漫談的第二篇,這一篇主要分析redux的思想和應(yīng)用,同樣參考了網(wǎng)絡(luò)上的大量資料,但代碼同樣都是自己嘗試實(shí)踐所得,在這里分享出來(lái),僅供一起學(xué)習(xí)(上一篇地址:個(gè)人博客/s...
閱讀 1816·2023-04-26 02:14
閱讀 3740·2021-11-23 09:51
閱讀 1393·2021-10-13 09:39
閱讀 3981·2021-09-24 10:36
閱讀 3022·2021-09-22 15:55
閱讀 3526·2019-08-30 12:57
閱讀 2045·2019-08-29 15:30
閱讀 1989·2019-08-29 13:19