Flux 深度解讀(翻譯)

FLux 是 facebook 用於構建 Web 客戶端的一種應用架構。它利用單向數據流,來幫助複雜的 React 組合組件的狀態管理。它是一種模式,而不只僅是一個框架,你能夠不須要寫任何新代碼來將 Flux 直接應用到你的應用當中。javascript

基於 Flux 的應用程序須要包含三個主要部分:dispatcher,store 和 view(React組件)。這並不能與 MVC 模式混淆,C 確實存在於 Flux 架構中,可是它們是 controller-views -- 一般在最頂層的視圖須要將數據傳遞給它的子組件。另外,action 的生產者 dispatcher 相關方法用於描述應用中各類可能的狀態改變。這對於咱們思考第四部分很是有幫助。html

Flux 避開了 MVC,採起了單向數據流,當用戶與 React 視圖進行交互的時候,視圖經過 dispatcher 方法傳遞一個 action 對象到保存數據和業務邏輯的各個存儲對象區 store 中。這些存儲區的數據變化會影響全部視圖,並致使視圖發生更新。這與 React 的編程風格有關,該風格容許經過數據的變化來改變視圖,而不須要指定如何經過狀態切換視圖。java

咱們最初的目的是可以正確的經過驅動數據:好比,咱們但願顯示消息線程未讀計數,而另外一個視圖顯示線程列表,高亮顯示未讀線程。這在 MVC 中很難處理 --- 將一個單一線程指派爲讀更新線程,而後須要更新未讀計數。這些依賴和關聯常常出如今大型 MVC 應用中。數據流和不可預測的操做交織在一塊兒。react

控制和存儲相反:存儲接受更新並在適當的時候進行協調處理,而不是在一致的依賴外部更新數據的方式。存儲區外部的任何東西都沒法觀察它內部的數據變化,這能夠幫助咱們保持清晰。存儲區中並無提供任何修改數據的方法,而是隻有一個簡單的途徑來將新數據推送到存儲區中 -- 註冊的 dispatcher。git

結構和數據流

在 Flux 應用中,數據的流向是單一的: github

單向數據流是 Flux 模式的核心,在上圖應該是__Flux 程序的主要模型__。dispatcher ,stores 和 views 是獨立的節點,具備不一樣的數據和輸出。actions 是簡單的對象,包括一個 type 標實屬性和新的數據。

視圖產生新的 action 在系統中傳播,以響應用戶的操做: npm

全部的數據流都要經過 dispatcher 來完成。action 在 action 生成器中被提供給 dispatcher,而且大部分來自於用戶的頁面交互操做。而後 dispatcher 執行在 store 上註冊的回調,將 action 傳入到全部的 store 中。在這些註冊的回調中,store 對 action 作對應的關聯處理。而後這些 store 通知一個變動事件,去告知 controller-views 數據發生了變化。controller-views 調用他們本身的 setState 方法,觸發一次從新渲染。更新整個相關的組件樹。
這種結構能夠幫助咱們很容易的預測程序的執行結果,這種方式讓咱們聯想起了函數式編程,或者數據流編程,基於數據的編程。在這些編程中,數據以單一的方向在程序中流動 -- 不存在雙向綁定。應用狀態僅存在 store 中,因此應用程序能夠不一樣程度的進行解藕。在 store 間保持着依賴關係,經過 dispatcher 進行同步的更新管理。

咱們發現,雙向數據綁定致使聯動更新,其中一個對象的改變就會致使另外一個對象改變,也可能出發更多的更新。隨着程序的增加,這些聯動更新變得難以預測。一次用戶操做會帶來複雜的數據更新,致使難以預測。可是,若是隻能單向的更新數據的話,這一切將變得更好預測。編程

讓咱們本身看看 Flux 的各個組成部分。redux

一個單一的 Dispatcher

dispatcher 做爲數據流管理程序中的派發器。它實際上是一個被註冊到 store 中的一個回調函數。並無本身的職能 -- 它將 action 傳入到 store 中。每個 store 都須要註冊並提供一個回調函數。每個 action 都是經過 dispatcher 被傳入到 store 中的。數組

隨着應用的迭代,dispatcher 變得更加劇要,它能夠用於在 store 間經過調用回調來按順序管理依賴關係。store 能夠等待其餘 store 完成更新,而後再更新本身。

Facebook 用於生產的 dispatcher 能夠在npm, Bower, 和 GitHub中找到。

Stores

store 中包含應用的狀態和邏輯。它的角色有點相似 MVC 中的 M,可是它們管理一些對象狀態 -- 他們不像 ORM 那樣表示單個數據記錄。這和 Backbone 的集合不一樣。和管理單一的 ORM 風格的數據對象集合不一樣。store 管理特定域內的數據狀態。

例如:Facebook 的 Lookback 媒體編輯工具使用了一個叫作 Lookback Video Editor 的技術來跟蹤回放時間和回放狀態。在另外一方面,同一個應用的鏡像存儲保持一個圖片的集合。在 TodoMVC 例子 這個例子的中, TodoStore 用於管理一個簡單的代辦事項集合。Store 便是一個數據集又是一個單模型域。

就像上面提到的,一個 store 會註冊一個 dispatcher 而且提供一個回調函數。這個回調函數接收 action 對象做爲參數。對於 store 中註冊的回調函數中,根據操做的類型提供一個 switch 操做來對 action 的 type 有針對性的進行處理。這容許操做經過 dispatcher 更新 store 中的數據狀態。以後,store 就進行了更新,他們經過廣播事件來通知它們的狀態發生了改變,所以視圖會查詢最新的狀態,而後更新本身。

Views and Controller-Views

React 提供了視圖層所須要的各類組合方式以及自由的視圖層渲染方式。在視圖層的最頂層,一種特殊的視圖用於監聽它所依賴的 store 中的廣播事件(註冊的回調)。咱們稱之爲 controller-view(react-redux的connectWrapper),由於它們提供了從 store 中獲取數據的方式,並將這些數據傳遞給它的後代。咱們也許須要一個 controller-view 來管理頁面的各個部分。

當它從 store 中接受到事件,它首先經過 store 提供的取數據方法拿到新的數據(mapStateToProps)。而後調用本身的 setState 或者是 forceUpdate 方法來讓 React組件及其子組件從新渲染。

咱們常常將整個 store 狀態傳遞給單個對象的視圖鏈中,容許不一樣的子組件使用它們須要的東西。除了將控制器的行爲保持在視圖結構的頂層,從而使咱們的後代視圖在功能上儘量的純粹以外,在單一對象中專遞整個狀態能夠減小咱們須要管理的數量。

有時候咱們須要在視圖結構中添加更多的 controller-view,以保持組件的單一性。這有助於咱們更好的封裝與特定數據相關聯的視圖的一部分。可是,請注意,在層次結構中更深的 controller-view 可能會爲數據流引入新的,可能衝突的數據。在決定是否須要添加深度 controller-view 時,要平衡簡單組件和不一樣位置流引入更多數據更新的複雜度。這些多個數據更新可能會致使奇怪的效果,經過來自不一樣的 controller-view 更新反覆調用 React 的 render 方法,這可能會增長調試難度(Redux經過單一數據源很好的解決了這個問題)。

Actions

dispatcher 提供一個對外的方法,咱們經過這個方法向 store 派發 action 對象。咱們會將生成 action 對象的邏輯封裝成一個方法,該方法觸發 dispatcher 並將 action 傳入 dispatcher。例如:咱們須要將代辦事項列表中的待辦事項內容。咱們須要在 TodoActions 中建立一個函數 updateText(todoId, newText),在這個函數中生成咱們須要的 action 對象,並將之綁定到 view 的一個交互事件上。當用戶觸發這個交互事件的時候,這個方法就被執行了,建立了 action 對象,並將其傳遞給 dispatcher,dispatcher 將 action 傳遞給 store,store 根據 action 中的 type 屬性進行數據處理。這個 type 屬性能夠像這個樣子 TODO_UPDATE_TEXT。

Action 也能夠從其它地方獲取,好比服務器。例如在初始化階段,當服務器返回錯誤代碼,或者服務器有數據更新的時候,就會發生這種狀況。

關於Dispatcher?

像以前提到的那樣,dispatcher 也能夠在相關的 store 之間進行分發。這個功能能夠經過 Dispatcher 類中的 waitFor 來完成,咱們不須要在一些簡單應用中這麼作。好比TodoMVC application,可是咱們在更大更復雜的應用中,這麼作是很是有必要的。

TodoStore 中註冊了一個回調,咱們能夠在這裏更新數據或者是作更進一步的處理:

case 'TODO_CREATE':
  Dispatcher.waitFor([
    PrependedTextStore.dispatchToken,
    YetAnotherStore.dispatchToken
  ]);

  TodoStore.create(PrependedTextStore.getText() + ' ' + action.text);
  break;
複製代碼

waitFor 接受一個參數,該參數是一個包含多個 dispatcher 的數組。所以,store 調用 waitFor 能夠像處理本身的數據狀態同樣處理其它 store 中的數據狀態。

在爲 Dispatcher 註冊回調的時候,它會返回一個 dispatcher token。

PrependedTextStore.dispatchToken = Dispatcher.register(function (payload) {
  // ...
});
複製代碼

更多有關 waitFor,actions,action creaters 和 dispatcher 的內容,請參考Flux: Actions and the Dispatcher

相關文章
相關標籤/搜索