從設(shè)計的角度看Redux
你知道 Redux 真正的作用遠(yuǎn)不止?fàn)顟B(tài)管理嗎? 你是否想要了解 Redux 的工作原理? 讓我們深入研究 Redux 可以做什么,它為什么做它的事情,它的缺點是什么,以及它與設(shè)計有哪些關(guān)聯(lián)?
你聽說過 Redux 嗎?它是什么? 請不要用 Google 搜索
- 花哨的后端的東西
- 我聽說過它,但我不知道它是什么,這可能是一個 React 框架
- 是一種在 React 應(yīng)用中存儲管理狀態(tài)的更好方式
這個問題,我問過 40 多位設(shè)計師,以上是他們的經(jīng)典回答。他們中的許多人都知道 Redux 與React 一起工作,它的工作是狀態(tài)管理。
本文的目的就是讓你對 Redux 有更全面的認(rèn)知: 它能做什么?為什么它要這樣設(shè)計?何時使用它?以及它與設(shè)計有哪些關(guān)聯(lián)?
我的目標(biāo)是幫助像你們這樣的設(shè)計師。即使您以前沒有寫過一行代碼,我認(rèn)為理解 Redux仍然是可能的、有益的和有趣的。
什么是 Redux
在超高水平上,Redux 是開發(fā)人員用來簡化他們工作的工具。你們很多人可能都聽說過,它的工作是狀態(tài)管理。稍后我將解釋狀態(tài)管理的含義, 此刻,我只能想讓你看下面這張圖:
為什么要了解 Redux
Redux 更多的是關(guān)于應(yīng)用程序的內(nèi)部工作而不是它的外觀和感受。 這是一個有點復(fù)雜的工具,學(xué)習(xí)曲線相對陡峭,但這是否意味著我們作為設(shè)計師應(yīng)該遠(yuǎn)離它?
不。我認(rèn)為我們應(yīng)該擁抱它。汽車設(shè)計師應(yīng)該了解引擎的用途,對嗎?為了成功地設(shè)計應(yīng)用程序界面,設(shè)計師還應(yīng)該對底層的東西有扎實的了解。我們應(yīng)該了解它可以做什么,理解開發(fā)人員為什么使用它,并了解它的優(yōu)勢和含義。
Redux 可以做什么
開發(fā)人員在 React 應(yīng)用中使用 Redux 來管理狀態(tài)。這最常見的用法,Redux 改進了React(尚未)做得不好的方面。
然而,你很快就會發(fā)現(xiàn) Redux 的真正功能遠(yuǎn)遠(yuǎn)不止于此,讓我們從了解狀態(tài)管理的真正含義開始。
狀態(tài)管理
如果你不確定這個狀態(tài)意味著什么,讓我們用一個更通用的術(shù)語來替換它:數(shù)據(jù)。狀態(tài)是不斷變化的數(shù)據(jù),狀態(tài)決定在用戶界面上顯示什么。
狀態(tài)管理是什么意思? 一般來說,我們需要在應(yīng)用程序中管理三個方面的數(shù)據(jù)
- 獲取和存儲數(shù)據(jù)
- 將數(shù)據(jù)綁定到 UI 元素
- 改變數(shù)據(jù)
比如我們要做一個 Dribbble 的作品頁面。在作業(yè)頁面上我們想要展示的數(shù)據(jù)有哪些?其中包括作者的頭像照片、名稱、動態(tài) GIF 圖片、點贊數(shù)量、評論,以及等等。
首先,我們需要從云服務(wù)器獲取所有這些數(shù)據(jù)并將其放在某個位置。接下來,我們需要實際顯示數(shù)據(jù)。我們需要將這些數(shù)據(jù)分配給對應(yīng)的 UI 元素,這些 UI 元素表示我們在瀏覽器中實際看到的內(nèi)容。例如,我們將頭像照片的 URL 分配給 img 標(biāo)簽的 src 屬性:
- <img src='https://url/to/profile_photo'>
我們需要處理對數(shù)據(jù)的更改。例如,如果用戶向Dribbble shot添加評論或點贊,我們需要更新相應(yīng)的 HTML。
協(xié)調(diào)狀態(tài)的這三個方面是前端開發(fā)的重要組成部分,React 對這項任務(wù)有不同程度的支持。有時候 React 中的內(nèi)置功能運行得足夠好。但隨著應(yīng)用程序變得越來越復(fù)雜,僅憑React 可能會更難管理它的狀態(tài)。這就是為什么許多人開始使用Redux作為替代。
獲取和存儲數(shù)據(jù)
在React中,我們將UI分解為組件。這些組件都可以分解為更小的組件。
圖片描述
如果我們的 UI 是這樣構(gòu)造的,那么在填充UI之前,我們什么時候獲取數(shù)據(jù)以及在哪里存儲數(shù)據(jù)
假設(shè)每個組件中都有一個廚師。從服務(wù)器獲取數(shù)據(jù)就好比是采購所需的所有原材料以準(zhǔn)備佳肴。
一種簡單的方法是在需要的地方和時間獲取和存儲數(shù)據(jù)。這就像每個廚師直接從遙遠(yuǎn)的農(nóng)場購買蔬菜和肉類一樣。
簡單方式: 每個組件各自獲取自己所需要的數(shù)據(jù)
這種方法是很浪費的。即使對于相同的數(shù)據(jù),我們也需要從多個組件多次請求服務(wù)器。廚師會浪費大量的汽油和時間來回奔波。
使用Redux,我們只獲取一次數(shù)據(jù)并將其存儲在一個中心位置,稱為 store。然后,任何組件都可以隨時使用這些數(shù)據(jù)。這就像附近有一家超市,我們的廚師可以在那里買到所有的食材。這家超市派卡車從農(nóng)場大批運回蔬菜和肉類。這比讓個別廚師親自去農(nóng)場效率高得多。
store 還是僅有的數(shù)據(jù)源。組件通常從 store 中獲取數(shù)據(jù),而不是其他地方。這使得 UI 保持高度統(tǒng)一。
Redux 將數(shù)據(jù)集中地存儲起來,并將數(shù)據(jù)分配給 UI 元素
將數(shù)據(jù)綁定到 UI 元素
如果單單使用 React 的話,實際上有一種更好的方法來獲取和存儲數(shù)據(jù)。我們可以請我們非常善良的廚師Shotwell為他所有的廚師朋友購物。他會開一輛卡車去農(nóng)場,把貨物運回。我們可以從容器組件中獲取數(shù)據(jù),例如 Dribbble 示例中的 Shot 組件,并將其用作單一的數(shù)據(jù)來源。
這種方法比從每個組件獲取數(shù)據(jù)的簡單方法更有效。但是 Shotwell 是如何將配料傳遞給其他廚師的呢? 如何將數(shù)據(jù)傳遞給實際渲染 HTML 元素的組件? 我們將數(shù)據(jù)從外部組件傳遞到內(nèi)部組件,就像接力棒一樣,一直傳遞到數(shù)據(jù)到達(dá)目的地。
例如,作者頭像的 URL 需要從 Shot 傳遞到ShotDetail、Title,傳遞到<img> 標(biāo)簽。如果我們的廚師住在公寓里,它看起來就像這樣:
要將數(shù)據(jù)交付到目的地,我們必須使用路徑上的所有組件,即使它們根本不需要數(shù)據(jù)。如果有很多層的話,那就太煩人了。
如果超市能送貨上門呢? 使用 Redux,我們可以將任何數(shù)據(jù)插入任何組件,而不影響其他組件,就像這樣
更準(zhǔn)確地說,實際上是另一個叫做 react-redux 的庫將數(shù)據(jù)提供給組件的,而并非 Redux 本身。但因為 react-redux 本身只是個連接庫,并且開發(fā)者通常一起使用 Redux 和 react-redux ,因此我認(rèn)為將它當(dāng)做是 Redux 的好處之一是并無不妥。
使用 Redux 將數(shù)據(jù)直接提取至目標(biāo)組件
注意:在React(16.3)的版本中,有一個新的 context API,它的提取數(shù)據(jù)功能幾乎與 Redux 是相同的。因此,如果你的團隊使用 Redux 的原因是為了提取數(shù)據(jù),不妨認(rèn)真考慮升級到 React 16.3!
改變數(shù)據(jù)
有時候,在應(yīng)用程序中更新數(shù)據(jù)的邏輯可能相當(dāng)復(fù)雜。它可能涉及多個相互依賴的步驟。在更新應(yīng)用程序狀態(tài)之前,可能需要等待多個服務(wù)器的響應(yīng)。我們可能需要在不同的時間、不同的條件下更新多處 state 的狀態(tài)。
如果我們沒有一個適合所有邏輯的良好結(jié)構(gòu),很容易讓人令人不知所措,代碼也很難理解和維護。
Redux 讓我們分而治之。 它提供了一種將數(shù)據(jù)更新邏輯分解為小“reducer”的標(biāo)準(zhǔn)方法。 這些 reducer 和諧地協(xié)同工作以完成復(fù)雜的動作。
Redux 的真正威力
到目前為止,Redux 看上去只是 React 的輔助工具。開發(fā)者使用它來解決 React 的某些痛點。但 React 正在快速著手解決這些問題!事實上,Redux 的作者 Dan Abramov 在幾年前已經(jīng)進入 Facebook 的 React 核心團隊。他們一直致力于提升 React 的開發(fā)體驗: context API (16.3版本發(fā)布)、更好的數(shù)據(jù)獲取 API (詳情請見 Dan Abramov 于2018年2月的演講)、更好的 setState API,等等。
它會使 Redux 過時嗎?
你猜怎么著? 我還沒有向你展示Redux的真正力量!
Redux 迫使開發(fā)人員遵循一些嚴(yán)格的規(guī)則,這給 Redux 帶來了強大的功能。
- 所有數(shù)據(jù)(應(yīng)用程序狀態(tài))必須以明文形式描述。 你應(yīng)該能夠用筆在紙上寫下所有數(shù)據(jù)。
- 每一個動作(數(shù)據(jù)的變更)都必須用清晰的文字來描述。你必須把你要做的事寫下來,然后再做改變。你不能改變數(shù)據(jù)而不留下痕跡。在 Redux 的術(shù)語中這稱之為 “派發(fā) (dispatching) 動作”。
- 更改數(shù)據(jù)的代碼必須像數(shù)學(xué)公式一樣。 在相同輸入的情況下,它必須返回相同的結(jié)果。 無論你運行多少次,4 的平方總是 16。
當(dāng)你遵循上述原則來開發(fā)應(yīng)用的話,不可思議的事情就來了。Redux 將開啟許多很酷的特性,這些特性使用其他技術(shù)很難實現(xiàn),或者實現(xiàn)起來成本很高。下面是一些例子。
我從 Dan Abramov 文章 “You Might Not Need Redux” 和 “React Beginner Question Thread.” 中收集了一些示例。
撤銷、重做
流行的 撤銷/重做 功能需要系統(tǒng)級規(guī)劃。因為撤銷/重做需要記錄和回放應(yīng)用程序中的每一次數(shù)據(jù)更改,所以你必須從一開始就在架構(gòu)中考慮到這一點。如果是事后才想到的,那就需要修改很多文件,這是無數(shù)錯誤的根源。
正因為 Redux 需要每個動作都以文本的形式進行描述,所以可以說是天生就支持撤消/重做。這個文檔中介紹了如何使用 Redux 來實現(xiàn)撤消/重做。
協(xié)作環(huán)境
如果你要構(gòu)建類似于 Google Docs 的應(yīng)用,其中多個用戶在復(fù)雜任務(wù)上協(xié)同工作,請考慮使用 Redux。 它能夠為你完成大量繁重的工作。。
Redux 可以非常輕松地通過網(wǎng)絡(luò)發(fā)送正在發(fā)生的事情。 接收另一個用戶在另一臺機器上執(zhí)行的操作,重放更改并與本地發(fā)生的操作合并是很簡單的。
OPTIMISTIC UI
Optimistic UI 是 Meteor 提出來的一種前端界面快速響應(yīng)用戶交互的概念,之前叫 Latency Compensation,主要作用是在客戶端直接響應(yīng)用戶的交互,而不用等信息從客戶端發(fā)送到服務(wù)器,完成更新確認(rèn),再從服務(wù)器返回客戶端這一個來回完成后再做響應(yīng)。有點類似游戲領(lǐng)域里的 Dead Reckoning,在客戶端離線對用戶行為進行推測,達(dá)到隱藏延時和減少帶寬使用的技術(shù)。
舉一個簡單的例子,在Twitter應(yīng)用程序中,你的點贊它需要請求服務(wù)器進行一些檢查,例如,該推文是否仍然存在。 Optimistic UI 的做法不是傳統(tǒng)的轉(zhuǎn)圈等待幾秒,然后顯示結(jié)果,而是選擇欺騙用戶!它事先假定所有請求都是成功的,當(dāng)用戶點贊時直接+1。
這種方式有效的原因在于大多數(shù)時候請求都是正常的。當(dāng)請求失敗是,應(yīng)用只需回滾至前一個 UI 狀態(tài)即可,并使用服務(wù)器響應(yīng)的實際結(jié)果,例如顯示錯誤信息。
如同撤消/重做一樣,Redux 也支持 Optimistic UI。 當(dāng)從服務(wù)器收到否定結(jié)果時,可以輕松記錄,重放和還原數(shù)據(jù)更改。
持久化和從狀態(tài)啟動
Redux 可以很容易地將應(yīng)用程序中發(fā)生的事情保存到本地存儲中。之后,即使電腦重啟,應(yīng)用程序也可以加載所有數(shù)據(jù),并從完全相同的位置繼續(xù)運行,就像從未中斷過一樣。
如果你使用 Redux 構(gòu)建游戲,則只需要幾行代碼來保存/加載游戲進度,而無需更改其余代碼。
真正可擴展的系統(tǒng)
使用 Redux,你必須“dispatch”一個 action 來更新應(yīng)用程序中的任何數(shù)據(jù)。 這種限制使我們可以深入了解應(yīng)用程序中發(fā)生的各個方面。
你可以構(gòu)建真正可擴展的應(yīng)用,其中每個功能都可以由用戶來自定義。例如,參考 Hyper ,這是一個使用 Redux 開發(fā)的終端應(yīng)用。“hyperpower” 插件增加了光標(biāo)的閃光點,并可以使窗口抖動。你是否喜歡這種 “wow” 模式呢?(或許這功能并沒有什么用,但卻是足夠吸人眼球)
圖片描述
時程調(diào)試(TIME-TRAVEL DEBUGGING)
當(dāng)調(diào)試應(yīng)用時能夠進行時間旅行會是怎樣一種體驗?運行應(yīng)用的過程中,隨意倒退或前進幾次以找到 bug 發(fā)生的確切位置,修復(fù) bug 后重放以確認(rèn)是否修復(fù)。
Redux 讓開發(fā)者夢想成真。Redux 開發(fā)者工具可以使開發(fā)者通過拖拽滑動條來操縱應(yīng)用的進度,就像 Youtube 視頻一般。
它是如何工作的? 還記得 Redux 強制執(zhí)行的三條嚴(yán)格規(guī)則嗎? 這是它的秘訣所在。
圖片描述
自動錯誤報告
想象一下:一個用戶在你的應(yīng)用程序中發(fā)現(xiàn)了一些錯誤,想要報告這個 bug。她煞費苦心地回憶和描述她所做的事情。然后,開發(fā)人員嘗試手動執(zhí)行這些步驟,以查看是否再次發(fā)生錯誤。錯誤報告可能是模糊的或不準(zhǔn)確的。開發(fā)人員很難找到 bug 所在的位置。
現(xiàn)在,這個怎么樣。 用戶單擊“報告錯誤”按鈕。 系統(tǒng)自動將她所做的事情發(fā)送給開發(fā)人員。 開發(fā)人員單擊“重播錯誤”按鈕并觀察錯誤是如何發(fā)生的。 bug 被當(dāng)場壓扁,每個人都很開心!
Redux Bug Reporter 就是這樣玩的。它的工作原理呢?Redux 的限制條件讓一切變成可能。
Redux 的缺點
Redux 執(zhí)行的三個主要規(guī)則是一把雙刃劍。它們支持強大的功能,但同時也帶來不可避免的缺點。
陡峭的學(xué)習(xí)曲線
Redux 的學(xué)習(xí)曲線比較陡峭。 理解,記憶并習(xí)慣其模式需要時間。 如果你完全不會 Redux 和 React ,不推薦你兩者同時學(xué)習(xí)。
“樣板” 代碼
在許多情況下,使用Redux意味著編寫更多代碼。通常需要接觸多個文件才能使一個簡單的功能正常工作。人們一直在抱怨他們必須用 Redux 編寫的樣板代碼。
我知道,這聽起來很矛盾。 我不是說 Redux 能夠用最少的代碼實現(xiàn)功能嗎? 這有點像使用洗碗機。 首先,你得花時間仔細(xì)地排列盤子。 在此之前,你將看到洗碗機的好處:節(jié)省實際清潔餐具的時間,消毒餐具等。你必須決定準(zhǔn)備時間是否值得!
性能損耗
由于其強制執(zhí)行的限制,Redux 也可能對性能產(chǎn)生影響。 每當(dāng)數(shù)據(jù)發(fā)生變化時,它會增加一點開銷。 在大多數(shù)情況下,這不是什么大問題,而且放緩并不明顯。 仍然,當(dāng)存儲中存在大量數(shù)據(jù)并且當(dāng)數(shù)據(jù)頻繁改變時(例如,當(dāng)用戶在移動設(shè)備上快速鍵入時),UI 可能因此變得緩慢。
Redux 不只是為 React 而生
一個常見的誤解是 Redux 僅用于 React。 聽起來Redux在沒有React的情況下無法做任何事情。 事實上,正如我們之前所討論的,Redux在幾個重要方面補充了React。 React 是最最常見的 Redux 用例。
然而,事實上,Redux可以使用任何前端框架,如Angular、Ember.js 甚至jQuery 或者 普通的JavaScript。試著谷歌一下,你會發(fā)現(xiàn)這個,這個,這個甚至這個。Redux 的一般思想適用于任何地方
只要你明智地使用 Redux,你可以在很多情況下得到它的好處,而不僅僅是在React應(yīng)用中。
總結(jié)
有不可避免的缺點。一個開發(fā)團隊的職責(zé)就是進行評估,看如何進行取舍并作出明智的選擇。
作為設(shè)計師,如果我們了解Redux的優(yōu)勢和劣勢,我們將能夠從設(shè)計的角度為這一決策做出貢獻。 例如,我們是否可以設(shè)計用戶界面以減輕潛在的性能影響? 也許我們可以提倡包含撤消/重做功能來刪除大量的確認(rèn)對話框? ?或許我們可以提倡 optimistic UI ,因為它能夠以相對較低的代價來提升用戶體驗。