原文連接: www.smashingmagazine.com/2018/07/red…javascript
推薦理由: 插圖大愛 沒有空洞的概念 也沒有海量的代碼!html
這個問題我曾問過不下於40個設計師。上面列出的是他們的經典回答。他們中很多人都知道 Redux 是和 React 一塊兒工做的,而且它的職責是「狀態管理」。前端
可是你可知道這個「狀態管理」的真正含義嗎?你是否知道 Redux 的真正威力遠不止狀態管理嗎?你是否知道 Redux 並不是必定要搭配 React 來使用?你是否想要加入團隊談論(至少是午飯討論),關因而否使用 Redux ?你是否想要了解 Redux 的工做原理?java
本文的目的就是讓你對 Redux 有更全面的認知: 它能作什麼?爲何它要這樣設計?什麼時候使用它?以及它與設計有哪些關聯?react
個人目標是幫助像你同樣的設計師。儘管你可能連一行代碼都沒寫過,不過我認爲仍是能夠理解 Redux的,並能從中受益和享受樂趣。貫穿全文的只有樸實的語言及有趣的塗鴉,沒有任何代碼及高談闊論。jquery
準備好了嗎?git
從大局來看的話,Redux 是一種讓開發者的工做更爲輕鬆的工具。正如你所聽過的,它的職責是「狀態管理」。稍後我將會解釋什麼是狀態管理。此刻,我只能想讓你看下面這張圖:github
🖼 Redux 用來管理狀態,但在狀態管理的背後,還有一些隱藏的能力 (Beebee 做圖)redux
Redux 所完成的工做,更多的是應用的內部運轉,而不是外觀界面。它是一個複雜的工具,學習曲線很陡。這是否意味着做爲設計師的咱們應該對它避而遠之呢?後端
不。我以爲咱們應該擁抱它。汽車設計師應該理解引擎是作什麼的,你說是嗎?要想成功地設計應用的界面,設計師應該清楚地瞭解應用背後的工做原理。咱們應該瞭解它能作什麼,理解開發者爲何要使用它,以及知道它的優勢和侷限性。
「設計不只僅是外觀感覺。設計關乎於工做原理。」
— 史蒂夫·喬布斯
許多人在 React 應用中使用 Redux 來管理狀態。這是最多見的用法,Redux 解決了 React 使用過程當中的一些痛點。
可是,很快你就會感覺到 Redux 的威力遠不止於此。咱們首先來介紹到底什麼纔是狀態管理。
若是你不肯定這個「狀態」到底表示什麼,那咱們用個更通俗的術語「數據」來進行替代。狀態是隨時間流逝而產生變化的數據。狀態決定了展現給用戶的界面。
到底什麼纔是狀態管理?一般來講,在一個應用中數據管理大體包括三方面的工做:
好比咱們要作一個 Dribbble 的做品頁面。在做業頁面上咱們想要展現的數據有哪些?其中包括做者的頭像照片、名稱、動態 GIF 圖片、點贊數量、評論,以及等等。
🖼 Dribbble 做品頁面的數據
首先,咱們須要從雲端服務器拉取這些數據並將其保存起來。接下來須要實際顯示數據。咱們須要將數據拆分開,而後分配給與之對應的 UI 元素,這些 UI 元素正是咱們在瀏覽器中實際所見的。例如,咱們將頭像照片的 URL 分配給 img
標籤的 src
屬性:
<img src='https://url/to/profile_photo'>
複製代碼
最後,咱們須要處理數據的變動。例如,若是用戶爲做品添加了一條評論或點贊,咱們須要更新相對應的 HTML 元素。
在前端開發過程當中,協調上述三方面的工做是一項大工程,React 對此有着不一樣程度的支持。有些時候,React 內置的功能就能很好的完成任務。但隨着應用變得愈發複雜,單單依靠 React 進行狀態管理會變得如履薄冰。這正是許多人開始使用 Redux 的初衷。
在 React 中,咱們將 UI 拆分紅組件。每一個組件又能夠拆分紅更小的組件。(參見 「圖解 React」)
🖼 Dribbble 的做品頁面拆分紅組件
頁面的結構是這樣的,那麼在渲染頁面以前咱們什麼時候去獲取數據呢?又將數據儲存在何處呢?
想象一下,每一個組件裏都住着一位大廚。從服務器獲取數據就比如是採購所需的全部原材料以準備佳餚。
一個簡單的辦法是在須要的時候纔去獲取數據,並將其就地儲存起來。這樣就比如每一個大廚都驅車前往郊外的農場來採購蔬菜和肉類。
🖼 簡單方式: 每一個組件各自獲取本身所須要的數據 (Beebee 做圖)
這種方式是一種浪費。有多少個組件咱們就得請求服務器多少次,即便是相同的數據。大廚們會浪費大量的汽油和時間在往返的路上。
使用 Redux ,咱們只獲取數據一次,並將數據存儲在一箇中心區域,一般稱之爲 「store」 。這樣數據對於任何組件來講均可以隨時使用。這就像附近有一家超市,咱們的大廚們能夠在那裏買到全部食材。超市會派卡車去農場大批量地運回蔬菜和肉類。這比每一個大廚都親自去農場採購要有效率得多!
store 仍是惟一的數據源。組件一般從 store 中獲取數據,而不是其餘地方。這使得 UI 保持高度統一。
🖼 Redux 將數據集中地存儲起來 (Beebee 做圖)
若是單單使用 React 的話,實際上有一種更好的方式來獲取並存儲數據。咱們能夠請求很是善良的大廚 Shotwell 來爲全部的廚師朋友們採購。他能夠驅車前往農場將貨物所有運回來。在 React 的世界中就是從一個容器型組件來獲取數據,例如,Dribbble 示例中的 「Shot」 組件就是容器型組件,可使用它來做爲單一數據源。
🖼 從根組件獲取數據 (Beebee 做圖)
這種方式要比每一個組件單獨去獲取數據要高效得多。可是大廚 Shotwell 如何將食材分給其餘大廚們呢?換句話說,如何將數據傳遞給實際負責渲染 HTML 元素的組件?將數據從外層組件傳遞給內層組件就比如是接力賽中的接力棒,一層層地傳遞下去直到抵達目的地。
舉個例子,做者頭像的 URL 須要從 「Shot」 傳出,而後傳到 「ShotDetail」,再到 「Title」 ,最後才能傳給 <img>
標籤。若是每一個大廚都住在公寓裏的話,應該就以下圖中展現的通常:
🖼 經過 props 將數據傳遞給目標組件 (Beebee 做圖)
要將數據傳遞給目標組件,咱們須要使用傳遞路徑上的全部組件,不管這些組件是否須要使用此數據。若是這個公寓是一個摩天大廈,那就太煩躁了!
若是超市送貨上門呢?使用 Redux 的話,咱們能夠將任意數據提取至任意組件而壓根不會影響到其餘組件,就像這樣:
更準確地說,其實是另外一個叫作
react-redux
的庫將數據提供給組件的,而並不是 Redux 自己。但由於 react-redux 自己只是個鏈接庫,而且開發者一般一塊兒使用 Redux 和 react-redux ,所以我認爲將它當作是 Redux 的好處之一是並沒有不妥。
🖼 使用 Redux 將數據直接提取至目標組件 (Beebee 做圖)
注意: 在 React 的16.3版本中,提供了一個新的 「context」 API ,它的提取數據功能幾乎與 Redux 是相同的。若是你的團隊使用 Redux 只爲提取數據的話,不妨認真考慮將 React 版本升至16.3!想了解更多詳情,請參見 官方文檔 (舒適提示: 文檔中有大量代碼) 。
有時候,在應用中更新數據的邏輯可能會至關複雜。它可能涉及到多個相互依賴的步驟。在更新應用的狀態以前,咱們可能須要等待多個服務器的響應。咱們還可能須要根據不一樣條件、在多個事件點更新狀態內的多處數據。
若是咱們沒有一個好的結構來實現全部這些邏輯,那將是毀滅性的,代碼將難以理解與維護。
Redux 可讓咱們進行分治。它提供了一種標準方式來將數據更新邏輯拆分紅衆多小塊的 「reducers」 。這些 reducers 能夠在一塊兒協調工做,以完成複雜的動做。
🖼 將複雜邏輯拆分紅 reducer (Beebee 做圖)
沒事能夠多關注一下 React 的開發進展。就像 「context」 API ,在 React 將來的版本中還可能會出現一個新的 「setState」 API 。它能夠將目前複雜的更新邏輯拆分紅一個個小塊。一旦這個新的 API 推出的話,極可能屆時將再也不須要 Redux 來管理狀態。
到目前爲止,Redux 看上去只是 React 的輔助工具。開發者使用它來解決 React 的某些痛點。但 React 正在快速着手解決這些問題!事實上,Redux 的做者 Dan Abramov 在幾年前已經加盟 Facebook 的 React 核心團隊。他們一直致力於提高 React 的開發體驗: context API (16.3版本發佈)、更好的數據獲取 API (詳情請見 Dan Abramov 於2018年2月的演講)、更好的 setState API,等等。
這是否意味着 Redux 將被淘汰?
你猜呢?我還未向你展現 Redux 的真正威力呢!
🖼 Redux 的威力遠不止狀態管理 (Beebee 做圖)
Redux 強制開發者遵循幾個原則,正是這些原則爲 Redux 帶來了強大的功能(這正是約束的力量!):
當你遵循上述原則來開發應用的話,難以想象的事情就來了。Redux 將開啓許多很酷的特性,這些特性使用其餘技術很難實現,或者實現起來成本很高。下面是一些例子。
我從 Dan Abramov 文章 「You Might Not Need Redux」 和 「React Beginner Question Thread.」 中收集了一些示例。
流行的撤消/重作功能須要系統級的規劃。由於撤消/重作須要記錄並回放應用中發生的每次數據變化,必須從一開始就在架構層面中考慮它。若是是過後才作,就須要修改大量的文件,這將致使層出不窮的 bugs。
🖼 撤消、重作 (Beebee 做圖)
正由於 Redux 須要每一個動做都以文本的形式進行描述,因此能夠說是天生就支持撤消/重作。這個文檔中介紹瞭如何使用 Redux 來實現撤消/重作。
若是你開發的應用相似於 Google Docs ,能夠多人協做來完成複雜任務,能夠考慮使用 Redux 。它可以爲你完成大量繁重的工做。
🖼 Google Docs (Beebee 做圖)
Redux 使得經過網絡來發送當前用戶正在作的事變得很簡單。接收到另外一個用戶在另外一臺機器上執行的操做後,重放另外一個用戶所作的更改,並與本地正在發生的動做合併起來是很容易的。
Optimistic UI 是一種提高應用用戶體驗的方式。它可使得運行在慢網速上的應用也能快速響應用戶的操做。在須要實時響應的應用中,這是一種流行的策略,例如第一人稱射擊遊戲。
🖼 Optimistic UI (Beebee 做圖)
舉個簡單例子,在 Twitter 應用中,當你點贊時實際上是須要請求服務器來作一些檢查的,例如當前推文是否存在。Optimistic UI 的作法不是傳統的轉圈等待幾秒,而後顯示結果,而是選擇欺騙用戶!它事先假定全部請求都是成功的,當用戶點贊時直接+1。
🖼 Twitter 點贊 (Beebee 做圖)
這種方式有效的緣由在於大多數時候請求都是正常的。當請求失敗時,應用只需回滾至前一個 UI 狀態便可,並使用服務器響應的實際結果,例如顯示錯誤信息。
如同撤消/重作同樣,Redux 也支持 Optimistic UI 。它使得一切都變得簡單起來,好比紀錄、重放和當請求失敗時進行回滾。
Redux 使得將應用中發生的一切紀錄並保存下來變得很是簡單。就算後面電腦重啓,應用也可以輕鬆加載全部數據,並從徹底相同的位置繼續運行,就好像它歷來沒有被中斷過同樣。
🖼 保存/加載遊戲進度 (Beebee 做圖)
使用 Redux 開發遊戲的話,只需少許代碼便可以保存/加載遊戲進度,而無需改變遊戲自己的代碼。
使用 Redux ,你必須 「dispatch」 動做才能更新應用中的數據。這一限制使得咱們能夠幾乎偵測和擴展應用中發生的一切。
你能夠構建真正可擴展的應用,其中每一個功能均可以由用戶來自定義。例如,參考 Hyper ,這是一個使用 Redux 開發的終端應用。「hyperpower」 插件增長了光標的閃光點,並可使窗口抖動。你是否喜歡這種 「wow」 模式呢?(或許這功能並無什麼用,但倒是足夠吸人眼球)
🖼 終端應用 Hyper 中的 「wow」 模式 (Beebee 做圖)
當調試應用時可以進行時間旅行會是怎樣一種體驗?運行應用的過程當中,隨意倒退或前進幾回以找到 bug 發生的確切位置,修復 bug 後重放以確認是否修復。
Redux 讓開發者夢想成真。Redux 開發者工具可使開發者經過拖拽滑動條來操縱應用的進度,就像 Youtube 視頻通常。
它是如何工做的呢?還記得 Redux 的三大原則嗎?它們正是祕訣所在。
🖼 在 Redux 開發者工具進行時間旅行 (Beebee 做圖)
想象一下,用戶發現應用中存在問題並想進行反饋。她煞費苦心地回憶和描述她所作過的一切。而後開發人員嘗試去手動執行這些步驟以查看 bug 是否復現。用戶的反饋極可能是模棱兩可的,因而開發人員很難找到 bug 出現的緣由。
若是是這樣呢,當用戶點擊 「反饋問題」 按鈕時,系統會自動地將用戶本地的狀態發送給開發人員。開發人員隨即點擊 「重發 bug」 按鈕即可查看 bug 到底是如何產生的。Bug 隨即被修復,你們都很開心!
Redux Bug Reporter 就是這樣玩的。它的工做原理呢?Redux 的限制條件讓一切變成可能。
🖼 自動反饋 Bug (Beebee 做圖)
Redux 的三大原則實際上是一把雙刃劍。它開啓強大功能的同時也不可避免地帶來一些反作用。
Redux 的學習曲線至關陡峭,須要時間去理解、記憶和熟悉它的模式。若是你徹底不會 Redux 和 React ,不推薦你二者同時學習。
在大多數狀況下,使用 Redux 就意味着要多寫不少代碼。一般須要編寫多個文件才能讓一個小功能運行起來。開發者一直都在抱怨使用 Redux 時所編寫的「樣板」代碼。
我知道,這彷佛有點自相矛盾。明明才說過 **Redux能夠省代碼,怎麼如今又來講Redux費代碼?**這就有點相似於使用洗碗機。首先,你得花時間仔細地排列盤子。直到完成洗碗你才感覺到洗碗機的好處,它節省了洗碗、餐具消毒等方面的時間。因此須要你來決定這個準備時間是否值得!
Redux 的三大原則會對性能產生一些影響。每當數據發生變化時,它會增長一點性能開銷。在絕大多數狀況下,這不是什麼大問題,性能損耗也不明顯。可是,當 store 中有大量數據而且數據變化頻率很是高的話(例如用戶在移動設備上頻繁地打字),UI 可能會變得卡頓。
一種常見的誤解就是 Redux 只是爲 React 提供的,若是離開了 React ,Redux 將毫無用處。確實,正如咱們以前一直所討論的,Redux 解決了 React 的一些痛點。React 是最最多見的 Redux 用例。
可是事實上,Redux 能夠和任何前端框架一塊兒使用,好比 Angular、Ember.js,甚至是 jQuery 或原生 JavaScript 。試試 Google 一下,你會發現 這個、這個、這個,甚至是這個。Redux 的思想能夠應用於任何地方 !
隨着你愈來愈普遍地使用 Redux ,你能夠在多種場景下享受它帶來的好處,而不只僅是在 React 應用中。
🖼 Redux 能夠搭配其餘前端框架一塊兒使用 (Beebee 做圖) 🖼 Redux 能夠搭配其餘前端框架一塊兒使用 (Beebee 做圖)
做爲一種工具,Redux 的使用效果是兩方面的。它開啓強大功能的同時也帶來了一些不可避免的缺點。一個開發團隊的職責就是進行評估,看如何進行取捨並做出明智的選擇。
做爲設計師,若是咱們可以理解 Redux 的優缺點,咱們就可以從設計的角度爲此決策做出貢獻。舉個例子,或許咱們設計出的 UI 可以緩解潛在的性能影響?也許咱們能夠提倡使用撤消/重作功能來替代大量的確認對話框?或許咱們能夠提倡 optimistic UI ,由於它可以以相對較低的代價來提高用戶體驗。
理解技術的好處與侷限性,並做出相應的設計。這正是我對斯蒂夫·喬布斯的名言 「設計關乎於工做原理。」 的解讀。