爲了方便理解,主要的英文名詞都沒有翻譯。好比:dispatcher(調度者)、 store(倉庫)、view(視圖)程序員
Flux是Facebook用來構建用戶端的web應用的應用程序體系架構。它經過利用數據的單向流動爲React的可複用的視圖組件提供了補充。相比於形式化的框架它更像是一個架構思想,不須要太多新的代碼你就能夠立刻使用Flux構建你的應用。web
Flux應用主要包括三部分:dispatcher、store和views(React components),千萬不要和MVC(model-View-Controller)搞混。Controller在Flux應用中也確實存在,可是是以controller-view的形式。view一般處於應用的頂層,它從stores中獲取數據,同時將這些數據傳遞給它的後代節點。另外,action creators - dispatcher輔助方法 - 一個被用來提供描述應用全部可能存在的改變的語義化的API。把它理解爲Flux更新閉環的第四個組成部分能夠幫助你更好的理解它。npm
Flux使用單向的數據流動來避開MVC. 當用戶與React視圖交互的時候,視圖會經過中樞dispatcher產生一個action。而後大量的保存着應用數據和業務邏輯的視圖接收到冒泡的action,更新全部受影響的view。這種方式很適合React這種聲明式的編程方式,由於它的store更新,並不須要特別指定如何在view和state中過渡。編程
咱們首創性的解決了數據的獲取:舉個栗子,好比咱們須要展現一個會話列表,高亮其中未讀的會話,同時展現未讀會話的數量。若是用MVC架構的話將很難處理這種狀況,由於更新一個對話爲已讀的時候會更新對話model,而後一樣也須要更新未讀對話數量model(數量-1)。這樣的依賴和瀑布式的更新在大型的應用中很是常見,致使錯綜複雜的數據流動和不可預測的結果。(這實際上是Facebook以前的一個線上bug,有時候用戶看到提示說有一條未讀信息,可是點進去卻發現沒有)。數組
反過來讓 Store 來控制:store接受更新,並在合適的時機處理這些更新。而不是採用一向依賴外部的方式來更新數據。在store外部,並沒辦法看到store內部是如何處理它內部的數據的,這樣的方式保證了一個清晰的關注點分離。Store並無相似setAsRead()這樣直接的setter方法,可是在其自成一體的世界中擁有惟一個獲取新數據的方法 - store經過dispatcher註冊的回調函數。服務器
在Flux應用中數據是單向流動的:架構
單向的數據流是Flux應用的核心特性,上圖應該成爲Flux程序員的主要心智模型。Dispatcher,stores和views是擁有清晰的輸入輸出的獨立節點。而actions是包含了新的數據和身份屬性的簡單對象。框架
用戶的交互可能會使views產生新的action,這個action能夠向整個系統中傳播:編輯器
全部的數據的流動都經過中樞dispatcher。Action能夠經過action creator產生並被提供給dispatcher,但多數狀況下action是經過用戶與views的交互產生。dispaer接收到action並執行那些已經註冊的回調,向全部stores分發action。經過註冊的回調,store響應那些與他們所保存的狀態有關的action。而後store會觸發一個 change 事件,來提醒controller-views數據已經發生了改變。Controller-views監聽這些事件並從新從store中獲取數據。這些controller-views調用他們本身的setState()
方法,從新渲染自身以及組件樹上的全部後代組件。函數
這種的響應式編程,或者更準確的說數據流編程亦或基於數據流的編程,可使咱們很容易去推斷咱們的應用是如何工做的。由於咱們的應用中數據是單項流動的,不存在雙向綁定。應用的狀態只保存在store中,這就容許應用中不一樣部分保持高度的低耦合。雖然依賴在store中也確實存在,但他們之間保持着嚴格的等級制度,並經過dispacher來管理同步更新。
咱們發現雙向綁定會致使瀑布式的更新,一個對象發生變化會引發另外一個對象的改變,並可能致使更多地更新。隨着應用的增大,這些瀑布流式的更新方式會使咱們很難預測用戶交互可能會致使的改變。當更新只能以單一回合進行的時候,系統的可預測性也就會變得更高。
讓咱們來看看Flux的各個部分。從diapatcher先開始會比較好
dispatcher 就像是一箇中央的集線器,管理著全部的數據流。本質上它就是 store callback 的註冊表,自己並無實際的高度功能。它就是一個用來向stores分發actions的機器。 每個 store 各自注冊本身的 callback 以提供對應的處理動做。當 dispatcher 發出一個 action 時,應用中全部的store都會經過註冊的callback收到這個action。
隨着應用的增加,dispacher會變得更加必不可少,由於它可以指定註冊的callback的執行順序來管理store之間的依賴。store能夠被聲明等待其餘store完成更新以後,再執行更新。
Facebook目前在生產環境中使用的flux能夠分別在npm, Bower,or Gihub中獲取。
Stores 包含了應用的狀態和邏輯,它有點兒像傳統MVC中的model層,可是卻管理這多個對象的狀態 - 他們不像傳統的ORM model 只管理單個的數據記錄,和backbone中得collection也不同。
舉個栗子,Facebook的回看視頻編輯器使用TimeStore來保存播放時間和播放狀態。另外,應用中的ImageStore保存着圖片的集合。再好比說咱們的TodoMVC示例中,TodoStore也相似地管理着to-do items的集合。store典型的特徵就是既是models的集合,又是所屬業務域下的model實例。
就像上面所說的,store在dispacher中註冊,並提供相應地回調。回調會接收action並把它當成本身的一個參數。當action被觸發,回調函數會使用swich語句來解析action中的type參數,並在合適的type下提供鉤子來執行內部方法。這就容許action經過dispacher來響應store中的state更新。store更新完成以後,會嚮應用中廣播一個change事件,views能夠選擇響應事件來從新獲取新的數據並更新。
React提供了一種可組合式的view讓咱們能夠自由組合展現層。在接近頂層的地方,有些view須要監聽所依賴的store的廣播事件。咱們稱之爲controller-view,由於他們提供了膠水代碼來從store中獲取數據,並向下層層傳遞這些數據。咱們會利用這些controller-views來處理頁面上某些重要部分。
當它接收到store的廣播事件後,它首先會經過store的公共getter方法來獲取所需的數據,而後調用自身的setState() 或 forceUpdate()方法來促使自身和後代的從新渲染。
在單一實例中,咱們一般會向後代view傳遞所有數據,而讓他們本身從中提取所需數據。此外咱們在結構的頂部也維持着相似controller的行爲,而且讓後代的view保持的function特性。經過向後代傳遞全部的數據,也有助於減小咱們須要管理的props的數量。
偶爾,咱們須要在系統的更深層的地方加入controller-views來保持咱們的組件的簡單。這有助於封裝一個特定的數據域下的相關部分。須要注意的是,系統深層的controller-views可能會影響數據的單向流動,由於他們可能會引入一些新的,潛在的存在衝突的數據流入口。在決定是否增長深層的controller-views時,咱們須要多方面權衡簡單的組件和複雜多樣的數據更新流這兩點。這些多樣的數據更新可能會致使一些古怪的反作用,伴隨着不一樣的controller-views的render調用,潛在的增長了Debug的難度。
dispatcher提供了一個能夠容許咱們向store中觸發分發的方法,咱們稱之爲action。它包含了一個數據的payload。action生成被包含進一個語義化的輔助方法中,來發送action到dispatcher。好比,咱們想更新todo應用中一個todo-item的文本內容。咱們會在TodoActions模塊中生成一個相似updateText(todoId, newText) 這樣的函數,這個函數能夠被視圖事件處理調用執行,所以咱們能夠經過調用它來響應用戶交互。action生成函數一樣會增長一個type參數,根據type的不一樣,store能夠作出合適的響應。在咱們的例子中,這個type能夠叫作TODOUPDATETEXT。
一個payload大概是這個樣子的: { source: "SERVER_ACTION", action: { type: "RECEIVE_RAW_NODES", addition: "some data", rawNodes: rawNodes } }
Actions也可能來自其餘地方,好比服務器端。這種狀況可能會在數據初始化的時候出現,也多是當服務器視圖更新的時候返回了錯誤的時候出現。
就像以前提到的那樣,dispatcher也能夠用來管理store之間的依賴。咱們能夠經過dispacher的waitFor()方法來實現。在相似TodoMVC這樣簡單地應用中咱們可能用不到這個方法,可是在更大型,更復雜的應用的它會變得不可或缺。
在執行TodoStore的註冊回調時,咱們能夠明確地等待任何依賴先更新,而後再進行後續的處理:
case 'TODO_CREATE':
Dispatcher.waitFor([
PrependedTextStore.dispatchToken,
YetAnotherStore.dispatchToken
]);
TodoStore.create(PrependedTextStore.getText() + ' ' + action.text);
break;
waitFor()的參數是一個包含了dispatcher註冊索引的數組,這個索引一般被稱之爲dispatch tokens。所以store可使用waitFor()來依賴其餘的state,以此來肯定如何更新它本身的state。
使用register()方法註冊回調的時候會返回一個id,這個id能夠用做dispatch token
PrependedTextStore.dispatchToken = Dispatcher.register(function (payload) {
// ...
});