在本系列的開始部分,我們將看到像 React.js 這樣的 UI 框架是如何引入一種替代方法,來取代使用 MVC 作為設計 Web 應用程序和 UI 的主要方法的。請繼續(xù)閱讀,了解更多信息。
Js、 Elm、 Cycle.js 和其他 UI 框架引入了一種構(gòu)建用戶界面的新方法。從函數(shù)式反應型編程到用戶界面開發(fā),它們甚至改變了我們對用戶界面的看法。這些方法很快就打破了 MVC 及其兄弟(MVP、 MVVM 等)看似不可避免的統(tǒng)治地位。本文是系列文章的第一篇,將簡要介紹這種構(gòu)建 UI 的新方法,并列出它與傳統(tǒng)方法相比的一些優(yōu)點。這些因素是如此強大,以至于在我看來,我們現(xiàn)在很有可能正在見證 MVC 時代的終結(jié)。
功能性反應用戶界面開發(fā)的概念
從表面上看,像 React.js 這樣帶有 Redux 架構(gòu)、 Elm 和 Cycle.js 的框架似乎完全不同。Redux 應用程序最初看起來類似于普通的 JavaScript 應用程序,可能主要關注函數(shù)式編程。Elm 應用程序有自己的語言,而 Cycle.js 應用程序只由反應流組成,這些反應流以驚人的方式結(jié)合在一起。
但是在表面之下,所有這些框架都有一個共同點: 功能性反應式 UI 開發(fā)的本質(zhì)。
上面的圖片大致概述了這些概念,它們在幾乎所有培養(yǎng)響應式編程的現(xiàn)代用戶界面框架之間共享。首先要注意的是,所有的事情——所有的變化、事件和更新——都朝著一個方向流動,形成一個循環(huán)。這篇文章將給出一個簡短的周期解釋,而后面的文章將進入更多的細節(jié)。
函數(shù)式反應式 UI 開發(fā)
這個循環(huán)由四個數(shù)據(jù)結(jié)構(gòu)(State、 Virtual DOM、 Event 和 Action)和四個組件(View ()-Function、 DOM-Driver、 ActionCreator 和 Updater)組成。DOM-Driver 由框架提供,而其他組件必須由應用程序開發(fā)人員實現(xiàn)。
假設我們的應用程序 todo-list 已經(jīng)運行了一段時間,用戶按下按鈕在 todo-list 中創(chuàng)建一個新條目。這將導致 DOM 中的按鈕單擊事件,DOM-Driver 捕獲該事件并將其轉(zhuǎn)發(fā)給我們的 ActionCreators 之一。
ActionCreator 獲取 DOM 事件并將其映射到操作。操作是命令模式的一個實現(xiàn),也就是說,它們描述了應該做什么,但是它們本身不修改任何東西。在我們的示例中,我們創(chuàng)建一個 AddToDoItemAction 并將其傳遞給 Updater。
Updater 包含應用程序邏輯。它保持對應用程序當前狀態(tài)的引用。每次它從 ActionCreators 之一接收到一個操作時,都會生成新的狀態(tài)。在我們的示例中,如果當前狀態(tài)包含三個 todo-item 并且我們收到 AddToDoItemAction,Updater 將創(chuàng)建一個新狀態(tài),其中包含現(xiàn)有 todo-item 和一個新狀態(tài)。
狀態(tài)被傳遞給 View ()-Function,它創(chuàng)建所謂的 VirtualDOM。顧名思義,Virtual DOM 并不是真正的 DOM,而是一種描述 DOM 應該是什么樣子的數(shù)據(jù)結(jié)構(gòu)。上面的代碼片段顯示了一個簡單的 的 Virtual DOM 示例。稍后的文章將詳細解釋 VirtualDOM 及其優(yōu)點。
VirtualDOM 被傳遞給 DOM-Driver,后者將更新 DOM 并等待下一個用戶輸入。有了這個,這個循環(huán)就結(jié)束了。
好處
功能性反應式 UI 開發(fā)相對于傳統(tǒng)方法有三個主要的優(yōu)勢,它們都是巨大的優(yōu)勢: 直接的測試、全面的事件流和時間旅行(是的,真的)。
簡單的測試
View ()-Function 和 ActionCreators 是簡單的映射,而 Updater 對它接收到的 Actions 執(zhí)行折疊(通常也稱為 reduce)。
所有組件都是純函數(shù),純函數(shù)非常容易測試。
純函數(shù)的結(jié)果只取決于輸入?yún)?shù),它們沒有任何副作用。要測試一個純函數(shù),只需創(chuàng)建輸入?yún)?shù)、運行“測試中的函數(shù)”并比較結(jié)果即可。沒有樣機,沒有依賴注入,沒有復雜的設置,沒有其他技術是必要的,沒有樂趣的測試。
綜合事件流
響應式編程有很多樂趣——除非它不是。圖形用戶界面的控制流本質(zhì)上是基于事件的。應用程序必須對來自用戶或服務器的按鈕單擊、鍵盤輸入和其他事件作出反應。應用反應技術,無論是觀察者模式、數(shù)據(jù)綁定還是反應流,都是自然而然的。
不幸的是,這些技術都是有代價的。如果組件 A 調(diào)用組件 B,則很容易在 IDE 或調(diào)試器中查看連接。但是,如果兩個組件通過事件連接起來,那么這種關系就不那么明顯了。應用程序變得越大,就越難理解其內(nèi)部結(jié)構(gòu)。
功能性反應應用程序的體系結(jié)構(gòu)通過定義所有組件都必須遵循的簡單事件流來避免這些問題。
無論應用程序的規(guī)模有多大,事件流永遠不會改變。
時間旅行
功能性反應式應用程序允許您在時間上來回旅行——至少在應用程序的上下文中是如此。如果我們存儲初始狀態(tài)和所有操作,我們可以使用一種稱為“事件采購”的技術。通過重播操作,我們可以重新計算應用程序所處的每個狀態(tài)。如果我們只重播最后的 n-1,n-2,n-3… 動作,我們實際上可以回到過去。通過修改記錄的行動流,同時應用它們,我們甚至可以改變過去。正如您可以想象的那樣,這在開發(fā)和修復錯誤時非常方便。
第一個時間旅行調(diào)試器已經(jīng)建立,但我認為我們才剛剛開始了解的可能性,更驚人的工具將在未來發(fā)布。
摘要
到目前為止,我們只觸及了功能性反應式 UI 開發(fā)的表面,但是到目前為止,應該很清楚這種方法具有一些巨大的優(yōu)勢。以后的文章將更深入地討論技術細節(jié),但也會展示其缺點(或者我們稱之為“尚未解決的挑戰(zhàn)”) ,并展示如何將所學到的經(jīng)驗教訓應用于 JavaFX 應用程序的示例。