- 原文地址:UNIDIRECTIONAL USER INTERFACE ARCHITECTURES
- 原文做者:André Staltz
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:EmilyQiRabbit
- 校對者:Hopsken,dandyxu
本文對所謂的「單向數據流」架構進行了非詳盡的概述。這並不意味着本文應被視爲一個初學者教程,它更應該是一個架構之間的差別和特性的概述。最後,我將會介紹一個和其餘框架顯著不一樣的新框架。本文僅假設客戶端是 Web UI 框架。html
若是沒有術語的共識,討論這些框架可能會形成困惑,因此咱們做出以下的假設:前端
用戶事件(User events) 是來自用戶直接操做的輸入設備的事件。好比:鼠標點擊,鼠標滾動,鍵盤按鍵,屏幕觸摸等等。react
當不一樣的框架使用 「View」 這個術語時,含義可能大不相同。做爲替代,咱們使用 「rendering」 來表明共識中的 「View」。android
**用戶接口渲染(User interface rendering)**指代屏幕上的圖形輸出,通常狀況下用 HTML 或者其餘相似的高級聲明代碼好比 JSX 來描述。ios
一個**用戶界面(UI)程序(User interface (UI) program)**是任何一個將用戶事件做爲輸入輸出視圖的程序,這是一個持續的過程而不是一次性的轉換。git
假定 DOM 以及其餘層好比一些框架和庫存在於用戶和架構之間。github
模塊間箭頭的所屬很重要。A--> B
和 A -->B
是不同的。前者是被動編程,然後者是反應式編程。這裏能夠閱讀更多。編程
若是子組件和總體的結構一致,這個單向架構就被稱爲分形(fractal)。redux
在分形架構中,總體能夠像組件同樣簡單地打包而後用於更大的應用。後端
在非分形架構中,那些不重複的部分被稱爲協調器(orchestrators),它們不屬於具備分級結構的部分。
第一個必須提到的是 Flux。它雖然不是絕對的先驅,可是至少在流行度上,對於不少人它都是第一個單向架構。
組成部分:
特色:
Dispatcher。 由於它是事件的載體,它是惟一的。不少 Flux 的變體去掉了對 dispatcher 的需求,其餘的一些單向框架也沒有 dispatcher 等同物。
只有 View 有可組合組件。 分級結構僅存在於 React 組件中,Stores 和 Actions 都沒有。一個 React 組件就是一個 UI 程序,而且其內部一般不會編寫成一個 Flux 架構的形式。因此 Flux 不是分形的,Dispatcher 和 Stores 做爲它的協調器。
用戶事件處理器在 rendering 中聲明。 換句話說,React 組件的 render()
函數處理和用戶交互的兩個方向:渲染和用戶事件處理(例如 onClick={this.clickHandler}
)
Redux 是一個 Flux 的變體,單例 Dispatcher 被改編成了一個獨一的 Store。Store 不是從零開始實現的,相反,建立它的方式是給 store 工廠一個 reducer 函數。
組成部分:
dispatch(action)
函數特色:
store 工廠。 使用工廠函數 createStore()
能夠建立 Store,由 reducer 函數做爲組成參數。還有一個元工廠函數 applyMiddleware()
,接受中間件函數做爲參數。中間件是用附加的鏈式功能重寫 store 的 dispatch()
函數的機制。
Providers。 對於用來做爲 UI 程序的 「View」 框架,Redux 並不武斷控制。它能夠和 React 或者 Angular 或者其餘框架配合使用。在這個框架中,「View」 是 UI 程序。和 Flux 同樣,Redux 被設計爲非分形的,而且以 Store 做爲協調器。
用戶事件處理函數的聲明可能在也可能不在 rendering。 取決於當下的 Provider。
Famous Framework 引入了 Behavior-Event-State-Tree (BEST),它是一個 MVC 的變體,BEST 中 Controller 分紅了兩個單向元素:Behavior 和 Event。
組成部分:
特色:
多範例。 State 和 Tree 是徹底聲明式的。Event 是急迫性的,Behavior 是功能性的。一些部分是響應式的,而其餘部分則是被動式的。(例如,Behavior 會對 State 做出反應,Tree 則對 Behavior 比較消極)
Behavior。 Behavior 將 UI 視圖(Tree)和它的動態屬性分離了,這在本文中的其餘幾個框架中都不會出現。據稱,這出於不一樣的考慮:Tree 就比如 HTML,Behavior 就比如 CSS。
用戶事件處理的聲明從視圖分離。 BEST 是極少的不將用戶事件處理和視圖關聯的單向框架之一。用戶事件處理屬於 Event,而不是 Tree。
在這個框架中,「View」 是一個樹結構,一個 「Component」 是一個 Behavior-Event-Tree-State 元組。組件是 UI 程序。BEST 是分形框架。
也被稱爲 「The Elm Architecture」,Model-View-Update 和 Redux 很類似,主要由於後者是受這個框架啓發的。這是一個純函數的框架,由於它的主語言是 Elm,一個 Web 的函數式編程語言。
組成部分:
特色:
處處都是分級結構。 以前的幾個框架只在 「View」 中有分級結構,可是在 MVU 架構中這樣的結構在 Model 和 Update 中也能找到。甚至是 Actions 可能也嵌套了 Actions。
組件分塊導出。 由於哪裏都是分級結構,在 Elm 架構中的 「component」 是一個元組,包括了:模塊類型,一個初始模塊實例,一個 View 函數,一個 Action 類型,一個 Update 函數。縱覽整個架構,不可能有組件從這個結構中偏離。每一個組件都是 UI 程序,而且這個架構是分形的。
Model-View-Intent 是基於框架 Cycle.js 的主要架構模式,它同時也是基於觀察者 RxJS 的徹底反應單向架構。可觀察(Observable) 事件流是一個全部地方都用到的原函數,Observables 上的函數是架構的一部分。
組成部分:
特色:
極大的依賴於 Observables。 該框架每一部分的輸出都被描述爲 Observable 事件流。所以,若是不用 Observables,就很難或者說不可能描述任何 「data flow」 或 「change」。
Intent。 和 BEST 中的 Event 大體類似,用戶事件處理在 Intent 中聲明,從視圖中分離出來。和 BEST 不一樣,Intent 建立了 actions 的 Observable 流,這裏的 actions 就和 Flux,Redux,和 Elm 中的相似。可是,和 Flux 等中的不一樣的是, MVI 中的 actions 不直接被髮送到 Dispatcher 或 Store。它們就是簡單的能夠直接被模塊監聽。
徹底反應。 用戶視圖反應到視圖輸入,視圖輸出反應到模塊輸出,模塊輸出反應到 Intent 輸出,Intent 輸出反應到用戶事件。
MVI 元組是一個 UI 程序。當且僅當全部用戶定義元素與 MVI 一塊兒應用時,這個框架是分形的。
這篇博文將 Nested Dialogues 做爲一個新的單向架構來介紹,適用於 Cycle.js 和其餘徹底依賴於 Observables 的方法。這是 Model-View-Intent 架構的一次進化。
從 Model-View-Intent 序列能夠函數化組合爲一個函數這個特性提及,一個 「Dialogue」:
如圖所示,一個 Dialogue 是一個將用戶事件的 Observable 做爲輸入(Intent 的輸入),而後輸出一個視圖的 Observable(View 的輸出)的方法。所以,Dialogue 就是一個 UI 程序。
咱們推廣了 Dialogue 的定義來允許用戶以外的其餘目標,每個目標都有一個 Observable 輸入和一個 Observable 輸出。例如,若是 Dialogue 經過 HTTP 鏈接了用戶和服務端,這個 Dialogue 就應該接受兩個 Observables 做爲輸入:用戶事件的 Observables 和 HTTP 響應的 Observables。而後,它將會輸出兩個 Observables:視圖的 Observables 和 HTTP 請求的 Observables。這個是 Cycle.js 裏面 Drivers 的概念。
這就是 Model-View-Intent 做爲 Dialogue 重組後的樣子:
要想將 Dialogue 方法做爲一個更大程序的 UI 程序子組件重複使用,這就涉及到 Dialogue 之間的嵌套問題:
Observables 在 Dialogues 不一樣層之間的鏈接是一個數據流圖。它並沒必要須是一個非週期圖。在例如子組件動態列表這樣的實例中,數據流圖就必須是週期的。這樣的例子超出了本文的討論範圍。
嵌套的 Dialogues 其實是一個元架構:它對組件的內部結構沒有約束,這就容許咱們將前文所述的全部架構嵌入一個嵌套的 Dialogue 組件中。惟一的約束涉及 Dialogue 的一端的接口:輸入和輸出都必須是一個或一組 Observable。若是一個結構如同 Flux 或者 Model-View-Update 的 UI 程序可以讓它的輸入和輸出都以 Observables 呈現,那麼這個 UI 程序就可以做爲一個 Dialogues 函數嵌入一個嵌套的 Dialogues。
所以,這個架構是分形的(僅涉及 Dialogue 接口時)、通常性的。
能夠查看 TodoMVC implementation 和 this small app 做爲使用了 Cycle.js 的嵌套 Dialogues 的例子。
儘管嵌套 Dialogues 的通常性和優雅性在理論上能夠用來做爲子組件嵌入到其餘架構中,但我對這個框架最主要的興趣在於構建 Cycle.js 應用。我一直在尋找一個天然且靈活的 UI 架構,而且同時可以提供 結構。
我認爲嵌套的 Dialogues 是天然的,由於它直接表現了其餘典型 UI 程序完成的:一個將用戶事件做爲輸入(輸入 Observable)持續運行的進程(Observable 就是持續的進程),而且產生視圖做爲輸出(輸出 Observable)。
它也是靈活的,由於正如咱們所見,Dialogue 的內部結構能夠自由的應用於任何模式。這和有着死板結構做爲條框的 Model-View-Update 截然相反。分形架構比非分形的更加易重用,我很高興嵌套的 Dialogues 也有這個屬性。
可是,一些常規的結構也能夠對引導開發有所幫助。雖然我認爲 Dialogue 的內部結構應當是 Flux,但我想 Model-View-Intent 很天然的適配了 Observable 的輸入輸出接口。因此當我想自由一些,不把 Dialogue 做爲 MVI 時,我認可大部分時間我都會把它構形成 MVI。
我不想自大的說這是最好的用戶界面架構,由於我也是剛剛發現了它而且依舊須要實際應用來發現它的優缺點。嵌套 Dialogues 僅僅是我如今的最強烈推薦。
若是你喜歡這篇文章,分享給你的 followers:(tweeting)。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。