摘要:作者小滬江前端開發(fā)工程師本文為原創(chuàng)文章,有不當(dāng)之處歡迎指出。于是,單一數(shù)據(jù)源規(guī)則實施起來,是規(guī)定用的頂層容器組件的來存儲單一對象樹,同時交給來管理。顧名思義,當(dāng)更新時,的回調(diào)函數(shù)會更新視圖層,以達到訂閱的效果。
作者:小boy (滬江web前端開發(fā)工程師)
本文為原創(chuàng)文章,有不當(dāng)之處歡迎指出。轉(zhuǎn)載請注明出處。
文章示例代碼:https://github.com/ikcamp/rea...
Redux 是近年來提出的 Flux 思想的一種實踐方案,在它之前也有 reflux 、 fluxxor 等高質(zhì)量的作品,但短短幾個月就在 GitHub 上獲近萬 star 的成績讓這個后起之秀逐漸成為 Flux 的主流實踐方案。
正如 Redux 官方所稱,React 禁止在視圖層直接操作 DOM 和異步行為 ( removing both asynchrony and direct DOM manipulation ),來拆開異步和變化這一對冤家。但它依然把狀態(tài)的管理交到了我們手中。Redux 就是我們的狀態(tài)管理小管家。
安利的話先暫時說到這,本期的技術(shù)周刊將為你帶來 React-Redux 在滬江前端團隊中的實踐。
0. 放棄你沒有看錯,在開始之前我們首先談?wù)撘幌率裁辞闆r下不應(yīng)該用 Redux。
所謂殺雞焉用宰牛刀,任何技術(shù)方案都有其適用場景。作為一個思想的實踐方案,Redux 必然會為實現(xiàn)思想立規(guī)矩、鋪基礎(chǔ),放在復(fù)雜的 React 應(yīng)用里,它會是“金科玉律”,而放在結(jié)構(gòu)不算復(fù)雜的應(yīng)用中,它只會是“繁文縟節(jié)”。
如果我們將要構(gòu)建的應(yīng)用無需多層組件嵌套,狀態(tài)變化簡單,數(shù)據(jù)單一,那么就應(yīng)放棄 Redux ,選用單純的 React 庫 或其他 MV* 庫。畢竟,沒有人愿意雇傭一個收費比自己收入還高的財務(wù)顧問。
1. 思路首先,我們回顧一下 Redux 的基本思路
當(dāng)用戶與界面交互時,交互事件的回調(diào)函數(shù)會觸發(fā) ActionCreators ,它是一個函數(shù),返回一個對象,該對象攜帶了用戶的動作類型和修改 Model 必需的數(shù)據(jù),這個對象也被我們稱作 Action 。
以 TodoList 為例,添加一個 Todo 項的 ActionCreator 函數(shù)如下所示:
在上例中,addTodo 就是 ActionCreator 函數(shù),該函數(shù)返回的對象就是 Action 。
其中 type 為 Redux 中約定的必填屬性,它的作用稍后我們會講到。而 text 則是執(zhí)行 “添加 Todo 項“ 這個動作必需的數(shù)據(jù)。
當(dāng)然,不同動作所需要的數(shù)據(jù)也不盡相同,如 “刪除Todo” 動作,我們就需要知道 todo 項的 id,“拉取已有的Todo項” 動作,我們就需要傳入一個元素為 Todo 項對象的數(shù)組( todos )。形如 text 、 id 、 todos 這類屬性,我們習(xí)慣稱呼其為 “ payload ” 。
現(xiàn)在,我們得到了一個 “栩栩如生” 的動作。它足夠簡潔,但擔(dān)任 Model 的 store 暫時還不知道如何感知這個動作從而改變數(shù)據(jù)結(jié)構(gòu)。
為了處理這個關(guān)鍵問題,Reducer 巧然登場。它仍然是一個函數(shù),而且是沒有副作用的純函數(shù)。它只接收兩個參數(shù):state 和 action ,返回一個 newState 。
沒錯,state 就是你在 React 中熟知的 state,但根據(jù) Redux 三原則 之一的 “單一數(shù)據(jù)源” 原則,Reducer 幽幽地說:“你的 state 被我承包了?!?/p>
于是,單一數(shù)據(jù)源規(guī)則實施起來,是規(guī)定用 React 的頂層容器組件( Container Components )的 state 來存儲單一對象樹,同時交給 Redux store 來管理。
這里區(qū)分一下 state 和 Redux store:state 是真正儲存數(shù)據(jù)的對象樹,而 Redux store 是協(xié)調(diào) Reducer、state、Action 三者的調(diào)度中心。
而如此前所說,Reducer 此時手握兩個關(guān)鍵信息:舊的數(shù)據(jù)結(jié)構(gòu)(state),還有改變它所需要的信息 (action),然后聰明的 Reducer 算盤一敲,就能給出一個新的 state ,從而更新數(shù)據(jù),響應(yīng)用戶。下面依然拿 TodoList 舉例:
當(dāng)接收到一個 action 時,Reducer 從 action.type 識別出該動作是要添加 Todo 項,然后路由到相應(yīng)的處理方案,接著根據(jù) action.text 完成了處理,返回一個 newState 。過程之間,整個應(yīng)用的 state 就從 state => newState 完成了狀態(tài)的變更。
這個過程讓我們很自然地聯(lián)想到去銀行存取錢的經(jīng)歷,顯然我們應(yīng)該告訴柜臺操作員要存取錢,而不是遙望著銀行的金庫自言自語。
Reducer 為我們梳理了所有變更 state 的方式,那么 Redux store 從無到有,從有到變都應(yīng)該與 Reducer 強關(guān)聯(lián)。
因此,Redux 提供了 createStore 函數(shù),他的第一個參數(shù)就是 Reducer ,用以描繪 state 的更改方式。第二個是可選參數(shù) initialState ,此前我們知道,這個 initialState 參數(shù)也可以傳給 Reducer 函數(shù)。放在這里做可選參數(shù)的原因是為同構(gòu)應(yīng)用提供便捷。
createStore 函數(shù)最終返回一個對象,也就是我們所說的 store 對象。主要提供三個方法: getState、dispatch 和 subscribe。 其中 getState() 獲得 state 對象樹。dispatch(actionCreator) 用以執(zhí)行 actionCreators,建起從 action 到 store 的橋梁。
僅僅完成狀態(tài)的變更可不算完,我們還得讓視圖層跟上 store 的變化,于是 Redux 還為 store 設(shè)計了 subscribe 方法。顧名思義,當(dāng) store 更新時,store.subscribe() 的回調(diào)函數(shù)會更新視圖層,以達到 “訂閱” 的效果。
在 React 中,有 react-redux 這樣的橋接庫為 Redux 的融入鋪平道路。所以,我們只需為頂層容器組件外包一層 Provider 組件、再配合 connect 函數(shù)處理從 store 變更到 view 渲染的相關(guān)過程。
而頂層容器組件往下的子組件只需憑借 props 就能一層層地拿到 store 數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)了。就像這樣:
至此,我們走了一圈完整的數(shù)據(jù)流。然而,在實際項目中,我們面臨的需求更為復(fù)雜,與此同時,redux 和 react 又是具有強大擴展性的庫,接下來我們將結(jié)合以上的主體思路,談?wù)勎覀冊趯嶋H開發(fā)中會遇到的一些細節(jié)問題。
2. 細節(jié) 應(yīng)用目錄清晰的思路須輔以分工明確的文件模塊,才能讓我們的應(yīng)用達到更佳的實踐效果,同時,統(tǒng)一的結(jié)構(gòu)也便于腳手架生成模板,提高開發(fā)效率。
以下的目錄結(jié)構(gòu)為團隊伙伴多次探討和改進而來(限于篇幅,這里只關(guān)注 React 應(yīng)用的目錄。):
入口文件 app.js 與頂層組件 react/container.js這塊我們基本上保持和之前思路上的一致,用 react-redux 橋接庫提供的 Provider 與函數(shù) connect 完成 Redux store 到 React state 的轉(zhuǎn)變。
細心的你會在 Provider 的源碼中發(fā)現(xiàn),它最終返回的還是子組件(本例中就是頂層容器組件 “Container“ )。星星還是那個星星,Container 還是那個 Container,只是多了一個 Redux store 對象。
而 Contaier 作為 業(yè)務(wù)組件 Wrapper 的 高階組件 ,負責(zé)把 Provider 賦予它的 store 通過 store.getState() 獲取數(shù)據(jù),轉(zhuǎn)而賦值給 state 。然后又根據(jù)我們定義的 mapStateToProps 函數(shù)按一定的結(jié)構(gòu)將 state 對接到 props 上。 mapStateToProps 函數(shù)我們稍后詳說。如下所見,這一步主要是 connect 函數(shù)干的活兒。
業(yè)務(wù)組件 component/Wrapper.js 與 mapStateToProps這兩個模塊是整個應(yīng)用很重要的業(yè)務(wù)模塊。作為一個復(fù)雜應(yīng)用,將 state 上的數(shù)據(jù)和 actionCreator 合理地分發(fā)到各個業(yè)務(wù)組件中,同時要易于維護,是開發(fā)的關(guān)鍵。
首先,我們設(shè)計 mapStateToProps 函數(shù)。需要謹記一點: 拿到的參數(shù)是 connect 函數(shù)交給我們的根 state,返回的對象是最終 this.props 的結(jié)構(gòu)。
和 Redux 官方示例不同的是,我們?yōu)榱丝勺x性,將分發(fā) action 的函數(shù)也囊括進這個結(jié)構(gòu)中。這也是得益于 bindActions 模塊,稍后我們會講到。
這樣,我們這個函數(shù)就準(zhǔn)備好履行它分發(fā)數(shù)據(jù)和組件行為的職責(zé)了。那么,它又該如何 “服役” 呢?
敏銳的你一定察覺到剛才我們設(shè)計的結(jié)構(gòu)中,以 “ params ” 開頭的屬性既沒起到給組件展示數(shù)據(jù)的作用,又沒有為組件發(fā)送 action 的功能。它們便是我們分發(fā)以上兩種功能屬性的關(guān)鍵。
我們先來看看業(yè)務(wù)組件 Wrapper :
現(xiàn)在,param 屬性們?yōu)槲覀冋故玖怂缪莸慕巧涸诮M件中實際分發(fā)數(shù)據(jù)和方法的快遞小哥。這樣,即使項目越變越大,組件嵌套越來越多,我們也能在 param.js 模塊中,清晰地看到我們的組件結(jié)構(gòu)。需求更改的時候,我們也能快速地定位和修改,而不用對著堆積如山的組件模塊梳理父子關(guān)系。
相信你應(yīng)該能猜到剩下的子組件們怎么取到數(shù)據(jù)了,這里限于篇幅就不貼出它們的代碼了。
Action 模塊: react/action.js、react/actionType.js 和 react/bindActions.js在前面的介紹中,我們提到:一個 ActionCreator 長這樣:
而在 Redux 中,真正讓其分發(fā)一個 action ,并讓 store 響應(yīng)該 action,依靠的是 dispatch 方法,即:
交互動作一多,就會變成:
而容易想到:抽象出一個公用函數(shù)來分發(fā) action (這里粗略寫一下我的思路,簡化方式并不唯一)
而細心的 Redux 已經(jīng)為我們提供了這個方法 —— bindActionCreator
所以,我們的 bindActions.js 模塊就借用了 bindActionCreator 來簡化 action 的分發(fā):
不難想象,action 模塊里就是一個個 actionCreator :
為了更好地合作,我們多帶帶為 action 的 type 劃分了一個模塊
react/reducers/ 和 react/store.js前面我們說到,reducer 的作用就是區(qū)別 action type 然后更新 state ,這里不再贅述。可上手實際項目的時候,你會發(fā)現(xiàn) action 類型和對應(yīng)處理方式多起來會讓單個 reducer 迅速龐大。
為此,我們就得想方設(shè)法將其按業(yè)務(wù)邏輯拆分,以免難以維護。但是如何把拆分后的 Reducer 組合起來呢 Redux 再次為我們提供便捷 —— combineReducers 。
只有單一 Reducer 時,想必代碼結(jié)構(gòu)你也了然:
我們最終得到的 state 結(jié)構(gòu)是:
state
demoAPP
當(dāng)有多個 reducer 時:
我們最終得到的 state 結(jié)構(gòu)是:
state
demoAPP
reducerB
想必你已經(jīng)想到更進一步,把這些 Reducer 拆分到相應(yīng)的文件模塊下:
接著,我們來看 store 模塊:
怎么和想象的不一樣?不應(yīng)該是這樣嗎:
這里引入 redux 中間件的概念,你只需知道 redux 中間件的作用就是 在 action 發(fā)出以后,給我們一個再加工 action 的機會 就可以了。
為什么要引入 redux-thunk 這個中間件呢?
要知道,我們此前所討論的都是同步過程。實際項目中,只要遇到請求接口的場景(當(dāng)然不只有這種場景)就要去處理異步過程。
前面我們知道,dispatch 一個 ActionCreator 會立即返回一個 action 對象,用以更新數(shù)據(jù),而中間件賦予我們再處理 action 的機會。
試想一下,如果我們在這個過程中,發(fā)現(xiàn) ActionCreator 返回的并不是一個 action 對象,而是一個函數(shù),然后通過這個函數(shù)請求接口,響應(yīng)就緒后,我們再 dispatch 一個 ActionCreator ,這次我們真的返回一個 action ,然后攜帶接口返回的數(shù)據(jù)去更新 state 。 這樣一來不就解決了我們的問題嗎?
當(dāng)然,這只是基本思路,關(guān)于 redux 的中間件設(shè)計,又是一個有趣的話題,有興趣我們可以再開一篇專門討論,這里點到為止。
回到我們的話題,經(jīng)過
這樣包裝一遍 store 后,我們就可以愉快地使用異步 action 了:
這里我們用 promise 方式來處理請求,model.js 模塊如你所想是一些接口請求 promise,就像這樣:
你也可以參閱我們往期介紹的其他方式。
最后,我們再來完善一下之前的流程:
3.結(jié)語Redux 的 API 一只手都能數(shù)得完,源碼更是精煉,加起來不超過500行。但它給我們帶來的,不啻是一套復(fù)雜應(yīng)用解決方案,更是 Flux 思想的精簡表達。此外,你還可以從中體會到函數(shù)式編程的樂趣。
一千個觀眾心中有一千個哈姆萊特,你腦海里的又是哪一個呢?
參考本文示例代碼GitHub地址:https://github.com/ikcamp/rea...
《Redux 官方文檔》
《深入 React 技術(shù)棧》
iKcamp原創(chuàng)新書《移動Web前端高效開發(fā)實戰(zhàn)》已在亞馬遜、京東、當(dāng)當(dāng)開售。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/85137.html
摘要:總結(jié)本文分析了在采用架構(gòu)下的數(shù)據(jù)設(shè)計結(jié)構(gòu),在一個復(fù)雜的場景下,希望引起讀者對能有一個更深入的認識。 前幾天刷Twitter,發(fā)現(xiàn)Nicolas(Engineering at @twitter. Technical Lead for Twitter Lite)發(fā)布了這么一條推文: showImg(https://segmentfault.com/img/remote/1460000009...
摘要:寫在最前原文首發(fā)于作者的知乎專欄中間件思想遇見的靈感附,感興趣的同學(xué)可以知乎關(guān)注,進行交流。其中,最重要的一個便是對多線程的支持。在中提出了工作線程的概念,并且規(guī)范出的三大主要特征能夠長時間運行響應(yīng)理想的啟動性能以及理想的內(nèi)存消耗。 寫在最前 原文首發(fā)于作者的知乎專欄:React Redux 中間件思想遇見 Web Worker 的靈感(附demo),感興趣的同學(xué)可以知乎關(guān)注,進行交流...
摘要:之前分享過幾篇關(guān)于技術(shù)棧的原創(chuàng)文章解析前端架構(gòu)學(xué)習(xí)復(fù)雜場景數(shù)據(jù)設(shè)計干貨總結(jié)打造單頁應(yīng)用一個項目理解最前沿技術(shù)棧真諦一個工程實例今天進一步剖析一個實際案例移動網(wǎng)頁版。目前面臨的問題在于提高產(chǎn)品的各方面性能體驗。 之前分享過幾篇關(guān)于React技術(shù)棧的原創(chuàng)文章: 解析Twitter前端架構(gòu) 學(xué)習(xí)復(fù)雜場景數(shù)據(jù)設(shè)計 React Conf 2017 干貨總結(jié)1: React + ES next ...
摘要:項目的架構(gòu)也是最近在各種探討研究。還求大神多指點項目技術(shù)總結(jié)技術(shù)棧項目結(jié)構(gòu)探究初體驗關(guān)于項目中的配置說明項目簡單說明開發(fā)這一套,我個人的理解是體現(xiàn)的是代碼分層職責(zé)分離的編程思想邏輯與視圖嚴格區(qū)分。前端依舊使用技術(shù)棧完成。 項目地址:https://github.com/Nealyang/R...技術(shù)棧:react、react-router4.x 、 react-redux 、 webp...
閱讀 3690·2021-11-22 15:24
閱讀 1593·2021-09-26 09:46
閱讀 1904·2021-09-14 18:01
閱讀 2601·2019-08-30 15:45
閱讀 3526·2019-08-30 14:23
閱讀 1866·2019-08-30 12:43
閱讀 2915·2019-08-30 10:56
閱讀 800·2019-08-29 12:20