flux詳細概述

翻譯:莫銘
原文地址:Flux-In Depth Overview

Flux是Facebook用來構建客戶端web應用的應用框架。它使用單向數據流來補充React的可組合視圖組件。它更像是一種模式,而不是一個正式的框架,你可以立即開始使用Flux,而不需要大量的新代碼。

Flux應用有三個主要部分: dispatcher, stores, 和views(React 組件)。不要將他們和Model-View-Controller混為一談。Controllers在Flux應用中是不存在的,他們以controller-views的形式存在(就是一種view,常見于層次結構的頂層,他們從stores接收數據,然后將數據向下傳給他們的子孫窗體)。另外,action的生成器(dispatcher的helper函數)作為語義API用來描述應用中可能出現的變化)。為方便理解也可以將它們視為Flux更新循環中的第四部分。

Flux避開MVC,而更傾向于單向數據流。當用于與React view交互時,view會通過dispatcher將action傳遞給每個store(這些store持有應用的數據和業務邏輯,并且更新那些受影響的views)。 這與React的聲明式編程風格特別相似,它允許store發送更新,而無需指定如何在states之間轉換views。

我們從恰當地處理派生數據開始:比如,我們想顯示消息線程的未讀信息數,而另一個視圖顯示線程列表,高亮顯示出那些有未讀消息的線程。使用MVC處理這種情況就比較困難(標記一個線程可讀,將會更新線程模型,進而需要更新未讀數模型)。這些依賴和級聯更新在大型的MVC應用中經常發生,致使數據流錯綜復雜,以及不可預測的結果。

Control和stores是相反的:stores接收更新,并根據需要進行調整,而不是依賴外部以固定的方式來更新它的數據。只有store自己(內部)才知道如何管理它所管理的數據,這有助于保持清晰的職責劃分。Stores沒有直接的setter函數(比如setAsRead()),取而代之的是在dispatcher中注冊回調函數,來獲取新的數據。

結構與數據流

Flux應用中的數據單向流動:



單向數據流是Flux模式的核心,上圖應該是Flux程序員心中主要的模型。dispatcher,stores和views是具有清晰輸入和輸出的獨立節點。actions是一個簡單的objects,包含新數據和一個標識類型的屬性。

views會根據用戶的交互,生成一個新的action在系統中傳播:



所有數據流經作為中央集線器的dispatcher。Actions通常來自于用戶與視圖的交互,并且在一個action創建器函數中提交給dispatcher。然后dispatcher調用store已經在dispatcher中注冊好的回調函數,store會回應那些與它維護的state相關的actions。然后,stores會發出一個更改事件,來告知controller-views,數據層發生了變化。Controller-views監聽這些事件,并在一個事件處理器(event handler)中從store獲取數據。controller-views調用他們自己的setState()函數,引起自身以及組件樹中自己的后代的重新渲染。


這種結構讓我們很容易理解我們的應用,以功能反應式編程或者數據流式編程的方式,數據在應用中以固定的方向流動,而不是雙向綁定。應用state只在store中維護,從而使應用的不同部分保持高度解耦。依賴只發生在store之間,通過dispatcher管理的同步更新,確保他們被留在嚴格的層次結構中。

我們發現雙向數據綁定會導致級聯更新,也就是改變一個對象,將會導致另一個對象的改變,進而觸發更多的更新。隨著應用規模的增長,這些級聯更新將會導致用戶交互結果的不可預測。但如果更新只會在一輪內更改數據,整個系統就會變得更加可預測。

讓我們仔細看看Flux的各個部分。dispatcher是一個好的切入點。

單獨的Dispatcher

在一個Flux應用中,dispatcher是管理數據流的中央樞紐。它并沒有真實的智能,本質上只是一個回調到store的注冊表。每個store注冊自己,并提供一個回調函數。當一個action創建器將一個新的action提供給dispatcher,應用中所有的stores,通過注冊的回調函數接收到該action。

隨著應用規模的增長,dispatcher變得更為重要,因為它可以通過這些注冊的回調函數,不同的調用順序,來管理stores間的依賴關系。store可以聲明式的等待其他store完成更新后,再相應的更新自己。

Facebook在產品用用到的同款dispatcher現在可通過npm, BowerGitHub獲得。

Stores

Stores擁有應用的state和邏輯。他們的角色有點類似于傳統MVC中的模型model,但是他們管理很多對象的狀態,不過不像ORM模型,他們不代表一條單獨的記錄數據。它們與Backbone的集合也不相同。除了簡單的管理ORM風格的對象集合外,store管理應用中特定域的應用狀態。

比如,Facebook的視頻回放編輯器利用一個TimeStore管理回訪時間點和回放狀態。另一方面,應用的ImageStore管理圖片集合。在我們TodoMVC示例中的TodoStore和他們差不多,它管理一個待辦項集合。一個store展現出的特性就是模型的集合和一個邏輯模塊單件。

如上所述,一個store通過dispatcher注冊自己,并提供一個回調函數。這個回調函數接收action作為參數。在store注冊的這個回調中,通過switch語法,判斷action的類型,用來區分action,來調用store的內部方法。在store更新后,他們廣播一個事件,來生命他們的狀態發生了改變,致使views可以查詢到新的狀態,然后更新自己。

Views和Controller-Views

React提供了視圖層所需的可組合且可自由重新繪制的視圖。在嵌套的視圖層次結構頂部,一種特殊的視圖監聽器,監聽那些依賴的store所廣播的事件。我們稱其為controller-view,因為它提供了粘合代碼,用來從stores獲取數據,然后將這些數據向下傳給它的后代鏈。我們可能會有一個用來管理頁面中任何重要部分的controller-view。

當它從store接收事件,它首先通過store公開的getter函數請求它需要的新數據。然后調用自身的setState()或forceUpdate()函數,致使運行自身的render()函數和它所有子孫的render函數。

我們一般將store的整個狀態放在一個單獨的對象中沿著view鏈向下傳遞,允許不同的子孫使用他們需要的數據。除了將類似controller的行為保留在層次結構的頂部,我們還要盡可能的使我們的后代視圖盡可能的只是功能,將store的整個狀態放在一個單獨的對象中向下傳遞,也有助于減少我們需要管理的props的數量。

偶爾我們可能也需要在更深的層次結構中添加額外的controller-views來保持組件的簡單。這會幫助我們更好的封裝層次結構中與特定數據域相關的部分。然而請注意,深層次的controller-views可能會由于引入了新的數據流入口,從而違背單一數據流的原則。在決定是否添加深度控制器視圖時,需要在獲取更簡單的組件和不同點流入層次結構的多數據更新的復雜性進行之間進行平衡。這些多數據更新將導致奇怪的效果,由于React的渲染函數被不同的controller-views重復的更新調用,從而可能增加調試的復雜度。

Actions

dispatcher公開一個方法,讓我們觸發一個派送到stores,并且包含一個裝載的數據,我們稱其為action。action的創建可以被包裝在一個語義helper函數中,將action發送到dispatcher。比如,在一個待辦列表應用中,我們打算改變一個待辦項的文本。我們可以在我們的TodoActions模塊中創建一個action以及一個函數簽名為updateText(todoId, newText)的函數。整個函數可以在我們的視圖事件處理器中調用,這樣我們就可以在用戶交互的響應中調用到它了。這個action創建函數也可以添加一個類型到action中,這樣當store解析它時,可以得到適當的處理。在我們的李子中,這個類型可以命名為TODO_UPDATE_TEXT.

Actions也可能來自其他地方,比如服務器。比如,發生在數據初始化時。也可能放生在服務器返回一個錯誤碼或服務器提供更新給應用。

Dispatcher哪?

正如之前所提到的,dispatcher也可以管理stores間的依賴。可以用Dispatcher類的waitFor()函數來實現此功能。在TodoMVC application這么簡單的應用中,我們確實不需要使用這個方法,但在更為大型和復雜的應用中,他非常的重要。

在TodoStore注冊的回調函數中,我們可以明確的等待依賴項先去更新,然后自己再往下執行:

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

  TodoStore.create(PrependedTextStore.getText() + ' ' + action.text);
  break;

waitFor()接收一個單獨的參數,該參數是dispatcher注冊索引數組,通常被稱作dispatch令牌。因此,調用waitFor()的store可以依賴另一個store的狀態來了解如何更新自己的狀態。

一個dispatch令牌是向Dispatcher注冊回調函數時由register()返回的:

PrependedTextStore.dispatchToken = Dispatcher.register(function (payload) {
  // ...
});

更多關于waitFor(),actions,action創建器和dispatcher,請查閱Flux:Actions和Dispatcher.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 去年翻譯的flux官方文檔對flux架構的描述,覺得最近很多朋友開始react編程了,所以我覺得有必要拿出來這篇水...
    余歌_非魚閱讀 2,579評論 0 9
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,886評論 18 139
  • 夏荷零落桂枝香,秋水潮青苦荻長。寒蟬猶吟楊柳曲,冷雨染罷梧桐黃。
    江南煙雨閱讀 509評論 1 22
  • 今天看了錦明老師的文章《要孩子改變,就必須先改變家長的聚焦總傾向》,認真讀完了錦明老師這篇文章,再一次體會到改變自...
    不忘初心堅持到底閱讀 145評論 0 2
  • 內容來自微博,自己收藏起來,用的時候更方便。
    賀頓閱讀 335評論 0 1