深度解讀當代前端架構進化史,下一個趨勢在哪

前端深閱讀 發佈 2020-01-22T03:07:15+00:00

作者 | Harshal Patil譯者 | 王強編輯 | 張之棟、王文婧早期的軟體架構模式建立在有限的硬體功能上並尊重這一事實,然而,今天的情況已經變了。計算能力在不斷提升,而軟體架構印證了這種觀點。本文從經典MVC說起,詳盡解讀了當代前端架構及下一階段的展望。


作者 | Harshal Patil

譯者 | 王強

編輯 | 張之棟、王文婧

早期的軟體架構模式建立在有限的硬體功能上並尊重這一事實,然而,今天的情況已經變了。計算能力在不斷提升,而軟體架構印證了這種觀點。本文從經典MVC說起,詳盡解讀了當代前端架構及下一階段的展望。對於前端工程師,以及想從宏觀層面理解現代Web應用程式架構的Web開發人員來說,均能從中獲益。

軟體架構的核心思想,就是推斷軟體系統各個組件之間數據流動的方式。

軟體架構的質量取決於你設法推斷這些數據流的難易程度!

本文要講的內容,就是在今天的 Web 應用程式背後探索這些數據流和最終的體系結構。Web 應用已從簡單的靜態網站(雙層結構)發展為複雜的多層次、SPA 和 SSR 驅動的 API 優先系統。CMS 系統已發展成無頭(Headless)和內容優先的系統。

近年來,前端社區的面貌日新月異。早年流行的是 jQuery 引入的 DOM 注入算法,其很快被基於 MVC 的 Backbone.js 所取代。之後一夜之間,我們就身陷於雙向和單向數據流架構的叢林之中。我們的成長足跡在某處中斷了。曾幾何時沉浸在 MVC 的世界是如何突然進入 React 開創的單向數據流時代的?它們之間有什麼聯繫?隨著本文的展開,我們將嘗試解開這個難題。

雖然本文針對的是前端工程師,但想要從宏觀層面理解現代 Web 應用程式架構的 Web 開發人員都能從本文中獲益。軟體體系背後有著大量活動——流程、需求收集、部署拓撲、技術棧等等,但那些已經超出了本文所涉及的範圍。

必要的前置知識——什麼是計算機?

計算機是一種從用戶收集數據 / 信息,並立刻或稍後將處理過的數據 / 信息提供給用戶的機器。計算機如何收集和展示這些數據呢?它使用軟體應用來實現這一目的。

軟體架構的關鍵是提供合理的手段來組成軟體,同時保持一切井然有序。

這裡的關鍵在於,軟體應用正在處理的數據被稱為模型或應用程式狀態。一些勇士可能將其稱為應用程式的域模型或業務邏輯。應用程式可以是桌面端也可以是 Web 端。

本文的宗旨是探索向(Web 前端的)用戶表示這種應用程式狀態的合理方式,同時讓一切井然有序。我們將探索數據如何從模型流向視圖層,僅此而已。

經典 MVC——起源

將數據與表示分離是(Web 端和桌面端)圖形用戶介面的核心思想。對於 MVC——模型 - 視圖 - 控制器來說,將表示(視圖)與關注域(模型)分離是其主導的設計理念。毫無疑問,MVC 是一項開創性的成果,其影響力綿遠流長。

如果要為軟體開發寫出第一原則,那麼它就會是 SoC——關注點分離。而且 MVC 模式可能是它第一個真正的落地實踐。

MVC 是為 Smalltalk-80 語言推出的。在 MVC 中,視圖(View)對象顯示模型(Model)對象持有的數據。在我們全面研究 MVC 中的數據流之前,我們必須了解當時(約 20 世紀 70 年代)的軟體應用環境:

  • 這個 MVC 僅適用於桌面應用。那時離 Web 誕生還有幾十年的時間。
  • 沒有 Web,複雜的 GUI 驅動的作業系統也不存在。
  • 這意味著應用軟體非常接近底層硬體。

以上這些限制對 MVC 影響很大。於是需要由控制器(Controller)對象負責響應鍵盤或滑鼠等用戶輸入並轉換為模型上的操作。此外,作業系統缺少 GUI 小部件意味著視圖與螢幕內容無法對應。

相反,視圖和控制器以配對的形式結合在一起。其中視圖部分向用戶顯示輸出內容,控制器部分接收來自用戶的輸入。應該注意的是,螢幕上的每個控制項都有一個視圖——控制器配對,這也是小部件(widget)理念的雛形。

今天,在 React、Vue 或 Angular 中,這種視圖——控制器配對與組件的思想是一樣的,儘管狀態處理層面的具體機制有所不同。

關於 MVC 就介紹到這裡,下面的圖片展示了 MVC 中的數據流示例。在這個例子中,我們有一個帶遞增和遞減按鈕的簡單計數器。計數器狀態維持著模型。此外,我們把兩個按鈕簡化成了一個來簡化敘述。

經典 MVC

在協作方面:

  1. 視圖和控制器包含對模型的直接引用,但反過來不行。這意味著模型不依賴 UI,可以在不擔心 UI 問題的前提下做更改。
  2. 模型實現了觀察者(Observer)模式,被一個或多個視圖對象註冊(subscribe)。當模型更改時會觸發事件,事件響應後視圖也會更新。

MVC 中有兩條數據流。在視圖流中不涉及模型,只涉及 UI 的更改。顯示按鈕單擊效果或對滑鼠滾動事件作出響應就是視圖流的例子。

MVC中的數據周期

今天我們不再使用這個 MVC 了,它有時被稱為經典 MVC 或老爹的 MVC。


開始使用應用程式模型

人們很快就意識到應用程式狀態無法與 GUI 完全隔離。我們總是要維護某種表示邏輯或視圖狀態。

複雜的圖形介面需要額外的狀態,這些狀態只用來輔助 UI 小部件,實現更好的用戶體驗。

但這可能會導致一些問題。來看看之前的計數器示例。當計數器達到 10 時,我們必須將標籤的顏色從黑色更改為紅色以表示警告。這種顏色變化行為實際上不是業務邏輯或關注點。這是純粹的美學部分(用戶體驗),需要加進來。真正的問題是——放在哪裡?是模型還是視圖?

由於這種表示邏輯或視圖狀態基本上是從域模型派生的狀態,因此必須在模型對象中維護它。但是域模型又要維護視覺部分(比如說紅色),這種定義就很尷尬。如果我們將它放在視圖對象中,那麼它會引入另一組問題。我們的標籤小部件不再是通用的了,無法在其他地方復用。此外,在視圖對象中放一個帶有硬編碼數字 10 的條件,意味著我們正在泄漏某些業務邏輯。

為了解決這個問題,我們在原始的 MVC 中添加了另一個實體——應用程式模型(Application Model,AM)。有了 AM 以後,視圖——控制器配對不再直接訪問模型了。相反,它們向 AM 事件註冊並使用它來訪問所需的數據。

應用程式模型MVC

數據流和經典 MVC 是一樣的。當然,每種模式都有其優點和缺點,AM-MVC 也不例外。最突出的問題是AM沒有對視圖對象的直接引用,因此即使 AM 被設計為維持視圖狀態,也無法直接操作後者。

總的來說,引入應用程式模型會使視圖特定的狀態遠離域層,並降低了視圖對象的複雜性來簡化視圖對象。這和表示模型(Presentation Model)很像,這是 Martin Fowler 在其開創性研究中創造的概念:

表示模型的本質是一個完全自包含的類,它表示 UI 窗口的所有數據和行為,但沒有任何用於在螢幕上渲染該 UI 的控制項。視圖只是將表示模型的狀態顯示在螢幕上。

現代桌面架構的時代

時光飛逝,世界也在不斷變化,新一代作業系統開始展現威力。應用程式遠離了底層硬體,它們之間加入了完整的內核、OS 驅動程序和實用程序。像 Windows 這樣基於 GUI 的作業系統提供了開箱即用的 UI 小部件。

不再需要控制器來監聽輸入設備了。視圖對象的理念改變了。

控制器的大多數功能都由作業系統接手。視圖的理念發生了變化:之前它只是一個小部件,現在它是一個眾多小部件的組合;一個視圖可以包含另一個視圖。視圖在某種意義上變成了雙向的,它響應用戶操作並顯示模型數據。

前端世界中視圖的理念與這個概念非常相似。在 Web 環境中,視圖是一個完整的頁面。

經典 MVC 變得過時且難用。為了適應這些不斷變化的環境,Dolphin 團隊在 1995 年正在尋找一種創建用戶介面的新模型。Dolphin 團隊如何找出新設計的歷史可參閱以下記錄,本文不再贅述。http://aspiringcraftsman.com/2007/08/25/interactive-application-architecture/

總而言之,該團隊最終將 MVC 模型旋轉了 60 度,他們稱其為 Twisting the triad。於是我們有了 MVP。

在協作方面:

  1. 表示器(Presenter) 監督表示邏輯。表示器可以直接更改視圖。視圖將用戶事件委派給表示器。
  2. 根據實現,視圖註冊到模型上,並依賴表示器處理複雜邏輯;或者在其他情況下,視圖只依賴表示器處理一切。

正如 Martin Fowler 在關於 GUI 架構的論文中總結的那樣,他將 MVP 實現分為監督控制器 MVP 和被動視圖 MVP。從圖中可以看出它們的差異和各自的數據流。

MVP——模型視圖表示器

MVVM——模型 - 視圖 - 視圖模型

MVP 很棒,有許多可能的變種和複雜的細節。但從前端應用程式的角度來看,MVVM 確實更勝一籌。在一些實現中,後者也被稱為模型 - 視圖 - 綁定器。MVVM 很像被動視圖 MVP,但增加了數據綁定的功能。它是一種編程技術,將來自提供者和消費者的數據源綁定在一起並同步。它擺脫了我們過去需要用來保持視圖和模型同步的許多樣板代碼。這樣我們就能在高得多的抽象級別上工作了。在協作方面:

  1. 視圖模型(ViewModel) 是一個對象,它公開視圖所使用的可綁定屬性和方法。
  2. MVVM 有額外的綁定器(Binder) 實體,負責使視圖與視圖模型保持同步。每次視圖模型上的屬性更改時,視圖都會自動更新以反映 UI 上的更改。

MVVM——Model View ViewModel

MVVM 中的數據綁定已經成為許多前端庫的基礎,包括 Knockout、Angular、Vue.js 和 React 等。

我們將在 Web 應用程式部分再討論數據綁定。

進入 Web 應用程式領域

就像原始的 MVC 模式一樣,出現了一種用於 Web 應用程式的模式。它被稱為 Web MVC。實際上,Web 應用程式的構建和部署方式讓 MVC 在 Web 端比在桌面端更加順其自然。

社區的主要困惑是不知道桌面 MVC 和 Web MVC 是兩種不同的模式。要是 Web MVC 當初不叫這個名字,大家就會清楚多了。

Web 應用程式是分布式應用程式的子類別。雖然 MVC 對 Web 應用程式感覺更自然一些,但一般來說構建分布式應用程式是很困難的。代碼的某些部分在伺服器上運行,同時還要保留在客戶端(例如瀏覽器)上。

大規模 Web 應用程式架構的重點在於確定代碼的哪個部分應該在哪裡執行。我們有服務端驅動的應用程式或富客戶端驅動的應用程式。在兩者之間,我們可以無限自由組合。

在 MVC 的語境中討論 Web 應用程式時有三個不同的數據周期,因此有三個 MVC 實現:服務端 MVC、瀏覽器內部的 MVC 和前端 MVC。瀏覽器負責三種類型交互的中介:

  1. 在客戶端(JS+HTML)代碼和服務端代碼之間。
  2. 在用戶和服務端代碼之間。
  3. 在用戶和客戶端代碼之間。

瀏覽器有自己的模型、視圖和控制器。作為開發人員,我們不必擔心瀏覽器 MVC。

服務端 MVC,亦即 2 號模型

服務端 MVC 的第一個眾所周知的實現是 Sun Microsystems 針對 Java Web 應用程式的 2 號模型(Model 2)

服務端 MVC——前端視角

這個 MVC 與傳統的 MVC 非常相似,但由於數據跨越客戶端和服務端邊界時數據流周期時間呈指數級上升,因此前者多出來很多複雜性。需要注意的一些事項有:

  • 桌面 MVC 有兩個數據周期,而 Web MVC 有三個數據周期。
  • 有兩種視圖周期。首先是客戶端視圖周期,例如滾動事件、鍵盤輸入等。其次是服務端視圖周期,例如頁面刷新、超連結激活等。
  • 最後我們有一個模型周期,它在通過客戶端 - 服務端邊界時有額外的時間複雜度。
  • 前端控制器(Front Controller):是通常由底層技術棧提供的組件,用於處理 HTTP 請求調度。例如 Java Web 應用程式中的 Servlet 容器、ASP.NET 中的 IHttpHandler 或 Node.js 中的 HTTP.Server 類:https://nodejs.org/api/http.html#http_class_http_server

今天的 SSR——服務端渲染則是完全不同的概念。然而事實並非如此。由於整個 HTML/ 內容是由服務端生成的,並且不涉及客戶端 JavaScript 代碼,因此完全使用服務端 MVC 構建的 Web 應用程式仍被視為 SSR。

超越服務端

從這裡開始就有意思了。不知不覺中,幾乎所有瀏覽器都開始提供 JavaScript 引擎。在我看來正是 AJAX 改變了 Web 應用程式。谷歌是最早的實踐者,在其郵件客戶端和地圖應用中採用了這項技術。

這個世界由服務端 MVC 生成 HTML+JS。JS 代碼遍布所有頁面。JavaScript 主要用來減少服務端視圖周期以改善用戶體驗。表單提交、輸入驗證等內容由客戶端代碼處理。

它是 Web 應用程式歷史上最流行的架構。大多數 B2C 應用程式、SEO 友好網站,尤其是使用 CMS(內容管理系統)構建的網站都在使用它。客戶端代碼量取決於應用程式的需求。

這樣的架構從未真正標準化,因此沒有名稱。它一直在漸進發展,仍被認為是 Web MVC 的擴展。ASP.NET MVC、Java Struts、Python Django、Ruby ROR 和 PHP CodeIgniter 是流行框架的一些例子,它們大量使用服務端 MVC 或 Web MVC。

當然,這種標準模式還有很多變體,但它們對當代前端架構沒有任何實際影響,可以忽略。

基礎 RIA——富網際網路應用架構

了解過這些背景,我們現在準備討論當代前端架構。當代前端架構主要解決的是構建 RIA——富網際網路應用程式的問題。RIA 的確切定義並不存在,因為很多事物都能用它描述。但總的來說,RIA 或富 Web 應用是應用程式的一個類別,其中應用程式嚴重依賴客戶端代碼,並且它們的用戶體驗非常接近桌面應用程式。它主要使用支持 SPA(單頁面應用程式)的框架構建。來自 Web MVC 的 服務端視圖周期的數據流這裡不存在。它通常只有一個初始 HTML 頁面,然後使用客戶端代碼和路由解決方案來渲染後續頁面。

構建 RIA 是一項複雜的操作,這種操作從以前基於桌面的 GUI 架構學習發展而來。視圖模型、觀察者、組件等就是從這些架構中借用的一些理念。Oliver steel 在他 15 年前的博客文章中(相信我,它是最出色的文章之一),為理解 RIA 數據流提供了很好的參考架構: https://blog.osteele.com/2004/08/web-mvc/

客戶端變重了——SPA 的時代

RIA 參考架構和 Web MVC 之間最顯著的區別是前者的頂層架構中缺少控制器和視圖。然而字面意義上講它們並沒有消失。如果我們看一下底層,控制器和視圖仍然存在,只是承擔的角色大大縮減了。後端主要是 API 驅動的。視圖僅限於生成 JSON,控制器負責編排傳入請求並將其映射到適當的業務工作流。

GUI 模式很難?

如果你已經深入探索了前面的模式,那麼你將意識到 GUI 模式與其他軟體工程模式有所不同。以可復用的面向對象軟體的元素為例:大多數模式都獨立於技術或語言,但 GUI 模式並非如此。這些模式適用於人機互動的邊界。用戶和副作用本質上是模式的一部分。注意:可復用的面向對象軟體的元素已經指出了經典 MVC 的本質。

GUI 模式適用於 HCI——人機互動的邊界。用戶和副作用本質上是模式的一部分。

因此,在不考慮基礎框架或語言的情況下幾乎不可能在理論上討論它們。到目前為止,我們可以在相當高的抽象級別上探索這些模式。但當我們接近本文的核心時,我們將依靠不同的庫或框架來描述這些模式。

大多數 Web UI 模式可以分為三個階段,即進化、變革和當代階段。

前端圖譜——宏觀視角

進化模式只是服務端 MVC 的擴展。它們並不會試圖改變方向或發明全新的事物。它們會一步一步地改進,以補足現有應用程式的缺陷。而變革模式是將前端應用開發與服務端驅動的工作流程分離的那些理念。它們的到來標誌著 SPA 應用的興起。當代模式就是對這些變革模式的二次修正,也是前端社區前進的大方向。

DOM 注入算法

由 jQuery 引入和掌握的這項技術是編寫大規模客戶端應用程式的真正起步,儘管 jQuery 並未真正解決架構問題。它旨在簡化當瀏覽器中存在太多不一致時的 DOM 操作。它提供了與瀏覽器無關的 API。

雖然我不覺得這是有意為之,但 jQuery 把 DOM API 簡化到了很難將其與正常的語言 API 區分的程度。這反過來又讓開發人員將 DOM API(視圖層)與業務邏輯(模型)完美地混合在一起。

需要指出的一點是,它仍然在同一服務端 MVC 的上下文中。這只是一個擴展。沒有真正的控制反轉。視圖 / 頁面的總體控制仍然由服務端代碼驅動。

jQuery——HTML 代碼

jQuery——DOM 注入算法

在上面的代碼片段中,模型、視圖和表示器 / 控制器被混合成一個單體代碼結構。當模型只包含一個屬性時就是這種情況。想像一下,嘗試構建一個沒有服務端視圖周期的 Web 應用程式(比如 SPA),做這樣的事情是沒有意義的。與 DOM 交互的代碼簡潔地注入了其他應用程式邏輯,這就是為什麼它被稱為 DOM 注入算法(注意:我不確定 DOM 注入這個術語是不是什麼規範名稱)

Backbone.js——MV*

正如我們在 jQuery 中看到的那樣,在開發應用程式時顯然缺少構建和組織代碼的方法。於是 Backbone.js 應運而生,成為了新的進化解決方案。它是首批將 MVC 風格帶給客戶端的庫之一。

Backbone.js 數據流

我們看一看 Backbone 數據流示意圖,就可以清楚地看到模型和視圖,但是沒有控制器的等效對象。模式在不斷發展,客戶端 MVC 只是之前 MVC 架構的進化。在這個進化過程中,大多數 JavaScript 社區都同意模型和視圖的定義,但針對控制器沒有形成共識。考慮到客戶端環境 ,控制器的理念並不合適。控制器留待進一步解釋。

至於 Backbone 而言,控制器不存在。那麼它適合哪裡?是 MVC、MVP 還是 MVVM?借用服務端 MVC 的定義,控制器有兩個職責:以傳入的 HTTP 請求的形式響應用戶操作,並協調模型以生成視圖(HTML 頁面)。在 Backbone 的情況下,視圖和路由器共享這些職責,但是缺少獨立的控制器或表示器的概念。

一些開發人員認為 Backbone 是 MVP,其中視圖類似於表示器,模板(Template)是視圖,而 Backbone模型和集合(Collection)構成模型。

正如 Addy Osmani 在他的博客中所說,最好不要強行把 Backbone 歸類到任何特定的設計模式。設計模式應被視為應用程式結構的靈活指南,由此看來 Backbone 既不像 MVC 也不像 MVP。相反,它借鑑了多種架構模式中的一些最佳概念,並創建了一個運行良好的靈活框架。

這就是 MV*模型 - 視圖 - 其他 的誕生歷程。強烈推薦 Addy Osmani 的博客文章:理解 MVC 和 MVP——適合 JavaScript 和 Backbone 開發人員。https://addyosmani.com/blog/understanding-mvc-and-mvp-for-javascript-and-backbone-developers/?source=post_page-----fb5b500b0231----------------------

與之前的 jQuery 相比,Backbone 可以生成更加結構化的代碼。

Backbone.js 中的視圖模板

在 Backbone.js 中創建一個模型

在 Backbone.js 中創建一個視圖實例

在 Backbone.js 中連結視圖和模型

前文中我將 Backbone 稱為下一個進化解決方案。原因是它只是補充並擴展了服務端 MVC。例如,如果我們的後端是 RESTful,意味著前端代碼只是用來表示服務端模型的手段,那麼 Backbone 已預置為與 API 同步:

Backbone.js 中自動生成的集合方法

而且 Backbone 中還有許多其他小型約定,感覺只是擴展而已。總之,Backbone 可能不是當時唯一的解決方案,但它在代碼結構和組織領域確實做出了開創性的工作。像 jQuery 一樣,它被許多產品採用。

Knockout.js——前端的數據綁定

Knockout.js 是我們基本模式的最後一個案例。它旨在為 JavaScript 實現 MVVM——模型 - 視圖 - 視圖模型。它也確實做到了。Backbone 解決的是代碼組織和結構的問題,而 Knockout 是在聲明式數據綁定的幫助下有效地實現視圖層。聲明式綁定的優點與其他聲明式編程結構相同:

  1. 易讀:聲明式代碼易於編程。
  2. 減少樣板:綁定允許我們在視圖模型更改時自動更新 DOM,並在視圖通過用戶輸入更改時更新視圖模型。
  3. 可觀察:Knockout 在事件之上提供更高級別的抽象。這允許 Knockout 自動跟蹤視圖模型props 之間的依賴項。如果需要,我們可以註冊可觀察屬性。

Knockout.js 視圖——雙向綁定

Knockout.js 中的視圖模型——使用計算屬性響應

雖然 Knockout 為視圖和視圖模型提供了定義良好的結構,但卻沒關注應用程式模型的問題。這使得 Knockout 高度專注、可以廣泛適應,因為它可以用作庫而非框架。我見過有人用它構建迷你 SPA 應用程式,其中 Web 應用程式有多個頁面,每個頁面都是一個小型的 Knockout 應用。這個StackOverflow 答案清楚地定義了 Knockout 的 MVVM 實現的範圍: https://stackoverflow.com/a/14516490/5723098

通常假設 Knockout 模型駐留在服務端。視圖模型只是使用 Ajax 或等效方式詢問服務端模型。

它在 DOM 更新用途上取代了 jQuery 和像 Handlebars 這樣的模板解決方案,但仍然使用 jQuery 來製作動畫、Ajax 等實用程序。與 Backbone 結合後,它可以作為 MVVM 模式的完整實現。理論上講這可能已成事實,但在此之前,這些概念已經被下一代工具鏈吸納過去了。

接下來 Angular 1、Aurelia、Ember.js 等代表的變革旅程開始了。

由於它與.NET 世界緊密相關,因此被廣泛用於 ASP.NET MVC 應用程式中。像 Backbone 一樣,它是另一種進化解決方案,針對的是稍微不一樣的問題。而且客戶端代碼依舊只是服務端 MVC 模式的擴展。服務端仍然是主導架構。彼時 Backbone 和 Knockout 常被拿來對比,但實際上它們是免費的解決方案。

Knockout 和 Backbone 都是 JavaScript 庫。不知何故,Backbone 被視為一個框架。為什麼?這裡沒有確定的答案,但可能是因為視角不同。由於強調代碼結構,Backbone 始終採用更高級別的抽象處理。此外,Backbone 從沒想過要取代無處不在的 jQuery(即使在 2019 年,最流行的 100 萬個網站中有七成都在使用 jQuery),而 Knockout 與核心 jQuery 功能高度重合(比如 DOM 操作),這自然讓 Knockout 舉步維艱。因此與 Backbone 相比,Knockout 的應用被大大局限了。但它仍然是前端社區首批實現的聲明式數據綁定之一,值得在這裡專門提到它。

Angular 1——給我控制權

jQuery 對 DOM 意味著什麼,Angular 1 就對一般意義的前端生態系統有著同樣的影響。它永久改變了構建大規模客戶端應用程式的概念。作為一個框架,它引入了許多概念——模塊系統、依賴注入、控制反轉和更簡單的數據綁定等。

曾幾何時,選擇正確的 JavaScript 庫,並為前端構建完美的技術棧仍然是一件痛苦的事。Angular 1 提供了一種更簡單但有凝聚力的替代方案。Ember.js 和其他類似的框架也可以這麼說,但是 Angular 1 的普及度是完全不同的層面,稱得上是那個時代的選擇。

從某種意義上說 Angular 1 是變革解決方案,它不再是對服務端 MVC 的簡單擴展,而是在頁面上灑滿了客戶端代碼。Angular 1 使 SPA 成為構建下一代用戶體驗的幾乎是事實上的一流解決方案。

是框架還是庫?

先前的解決方案更像是庫而不是框架。毫無疑問,Angular 1 是一個明確定義的框架。框架和庫之間的關鍵區別是 IOC——控制反轉。作為框架,Angular 1 具備:

  1. 明確定義的 MVVM:Angular 1 具有清晰的模型、視圖和視圖模型對象。
  2. 依賴注入(DI):Angular 1 對 DI 提供一流支持,為模型對象提供生命周期管理。在 Angular 1 中,應用程式的模型使用服務(Service) 實現。默認情況下服務是一個單例實體,對於模型對象來說通常很理想。
  3. 數據綁定和更改檢測:數據綁定是聲明式的,更改檢測是自動的。數據綁定是 MVVM 必要的組件。綁定是雙向的。此外更改檢測幾乎是自動的(Angular 1 真的很神奇)。它為開發人員省去了許多樣板代碼。事件的使用和整體註冊機制往往也會簡化。這在基於 MVC 的系統中非常普遍。事件還會降低代碼可讀性,因為某些工作流的數據流分布在代碼中。
  4. 模塊系統:Angular 1 引入了特定於框架的模塊系統。模塊是幾乎所有語言的代碼組織的基礎。JavaScript 直到 2015 年才有模塊系統(瀏覽器直到 2018 年才支持它)。Angular 在組織方面領先時代。

與此同時,Angular 1 也因其引入的複雜性而受到批評。最重要的批評指出它是在服務端構造之後建模的,前端開發人員並不習慣這樣,有些缺陷無法忽視:

  1. 命名空間衝突:雖然 DI 很棒,但它是使用全局命名空間的 Service Locator 模式實現的,於是服務必須添加前綴。
  2. 雙向數據綁定:當時這可能是個好主意,但之後人們意識到它並不能真正擴展。特別是 React 的興起使雙向數據綁定成為可選項。不小心的話,雙向綁定可以帶來許多麵條代碼。
  3. 令人困惑的術語:Angular 1 使用的一些術語顯然令人困惑。例如,Angular 1 用 $scope 作為視圖模型,但它也有控制器可以擴充 $scope 對象。可能正確的名稱可以是 VMFactory 之類。此外,Angular 1 有三種服務配方,其中一種名為 Service。

還有許多其他小問題。另外,Angular 2 或簡稱 Angular 是一個徹底的更新,它就像一個全新的框架。除了名字和少量概念之外,兩代版本之間找不到什麼共同點。

Angular.js——概覽

多年來,Angular 1 發布了一些小版本更新,修復了許多複雜的小細節。最重要的是增加了組件模型,組件模型是前端世界大多數理念的交匯點。

Angular 1 在前端社區的遺產影響深遠。它的優缺點幫助社區了解了軟體架構的重要性,並為編寫可擴展應用程式的工作提供了基準。它的缺點 / 不足成為了未來架構解決問題的基礎。

當代前端架構

現代應用程式在各個層面都非常接近桌面應用程式。當今環境中的一些顯著變化包括:


  1. Web 不再局限於台式機平台。我們的設備類型多種多樣,如平板電腦、手機、智能手錶和智能眼鏡等等。事實上,智慧型手機驅動的 Web 流量超過了台式機平台。
  2. 作為 Web 的支柱,JavaScript 已經發展成為一種成熟的語言,並且還在不斷發展,不斷引入眾多新功能。
  3. 文件系統、相機、PWA 和硬體傳感器等新型 API 都能用在 Web 應用程式中。
  4. 用戶體驗是用戶在互相競爭的服務之間做出選擇的決定性因素。
  5. 網絡基礎設施有所改善,但與此同時又有數以十億計的收入底層用戶正在加入 Web 世界。流媒體和視頻點播都變成了日常事務。
  6. 人們基於同一套 JS 技術棧來使用各種工具和交叉編譯器構建原生移動應用程式。
  7. 許多新的框架和工具使用 JavaScript 作為目標語言,而非將其用作源語言。一些不錯的例子有 Elm、PureScript 和 ReasonML 等等。

當代前端架構是這些不斷變化的需求的反映。顧名思義,它們建立在整個前端社區從過去學到的經驗之上。

早期的軟體架構模式建立在有限的硬體功能上並尊重這一事實。今天的情況已經變了。計算能力在不斷提升,而且軟體架構反映了這種觀點。

以上假設可以與今天的前端架構相關聯。這些架構融合了三大核心原則。

數據流占據舞台中心

考慮到前端代碼需要運行的領域眾多、環境多樣,業務邏輯占據了中心位置。作為工程師,我們花費更多時間來閱讀和調試,而非編寫代碼。我們必須直觀地跟蹤代碼。每當修復錯誤或向現有代碼庫添加新功能時,了解其可能導致的影響和回歸是至關重要的。

在大型代碼庫中做到這一點的唯一方法是確保 我們了解數據在應用程式中的流動方式。這是軟體架構最重要的目標。

今天,任何框架都是圍繞這條關鍵原則構建的。明確區分狀態視圖是非常重要的——鑒於簡單的基於事件的容器在向更複雜的狀態容器(如 ReduxVuexNgrx 等)轉變,這也是顯而易見的。由於我們嚴重依賴事件發布/訂閱系統,數據(或控制流)將流經應用程式的每一寸角落。對數據流的重視不再局限在局部環境。相反,作為一種核心思想,現在我們要在整個應用程式中考慮數據流。

Angular 1 已經證明雙向數據流(即時只局限在視圖或組件上)可以帶來紛繁複雜的控制流。此外,React 已經證明單向數據流很容易推斷,因此包括 Angular 2 在內的現代框架也遵循了這一理念。

基於組件的架構

轉向基於組件的用戶介面是數據流第一原則的必然結果。在談論組件時,有三個方面需要考慮。

首先是數據流的重點:傳統架構側重於水平分工。但是基於數據流的思維要求垂直分工。在敏捷世界中,這就是我們設想用戶故事的方式。基於 MVC 的架構不容易做到這一點。沒有簡單的方法可以通過一項功能共享或復用 UI 代碼(這裡的問題是——當功能在橫向組織推動下在整個代碼庫中傳播時,我們怎樣才能真正隔離功能呢!)。但是,封裝入一個完整功能的組件可以輕鬆配置為可共享和可打包的實體。

其次是不斷發展的視圖狀態:過去,視圖狀態業務狀態相比占比較少。但隨著應用程式變得更具交互性,其占比也得到了爆髮式增長。視圖狀態需要接近實際視圖。視圖狀態很複雜,通常表示必需的時間關聯數據,如動畫和轉換等。類似 MVC 的架構沒有什麼很好的方法來封裝這種狀態。在這裡,作為封裝核心單元的組件非常有用。

第三個方面與 UI 開發的原子單元有關。首先我們提出一個問題:

共享 UI 功能的最佳方式是什麼?共享 UI 功能意味著它可以包含以下四個部分:

結構(HTML—視圖)、樣式(CSS—視圖)、行為(JavaScript—視圖模型)和業務邏輯(模型)。

於是組件的概念應運而生。我們意識到組件只是 MVVM 模式MV* 模式 的一個很好的實現……

但具體而言,如何表示 UI 組件?我能找到的最接近的概述是 Deric Baily 的博客文章:

https://derickbailey.com/2015/08/26/building-a-component-based-web-ui-with-modern-javascript-frameworks/

在語言級別,通常使用模塊或包來表示可復用功能的構建塊。JavaScript 也有模塊。但這還不夠。

組件的這種抽象概念允許框架作者根據他們的需要定義具體的實現。

此外,良好的組件是高度可組合的,可實現分形架構。例如,登錄表單組件可以是標準登錄頁面的一部分,或者在會話超時時顯示為對話框。一個組件只要定義好接收的屬性和發送的事件,就可以在滿足基礎框架要求的前提下復用在任何地方。

組件可以是由 Webpack 等打包器生成的函數、類、模塊或打包代碼。

如前所述,組件只是一個 MVVM 模式的良好實現。並且由於組件的可組合性,我們將 MVVM 模式實現為分形(分形是一種自我重複的無止境模式)。這意味著我們在多個抽象級別處理多個獨立的的 MVVM 控制流。

實現 MVVM 的無限組件分型——循環套著循環!是不是很像《盜夢空間》?

而且,這正是我們對架構所期望的。

一個好的架構可以實現多級抽象。它允許我們一次查看一個抽象(細節級別)而不必擔心其他級別。這是製作可測試和可讀解決方案的關鍵所在。

讓框架處理 DOM

DOM 用起來既昂貴又繁瑣。當狀態本地全局)發生變化時,DOM 能以某種方式自動更新就最好了。此外,它應儘可能高效同時不干擾其他元素。在將 DOM 與狀態同步時有些事情需要注意。

  1. 將 DOM 表示為模型的函數:與組件相結合,聲明式數據綁定是用模型表示視圖的一個很好的解決方案。Angular 有模板,React 使用 JSX,Vue 支持 JSX 和模板。結合數據綁定和組件,我們得到了一個完美的 MVVM。
  2. 更改檢測:框架需要一種機制來識別狀態中所有數據的變化。Angular 1 使用的是昂貴的消化周期,React 則支持不可變數據結構。使用不可變數據檢測狀態更改只是一個等式檢查而已。不需要髒檢查了!Vue.js 依賴建立在 getter/setter之上的反應系統;Angular 使用區域檢測更改;Vue 3 將使用 ES2015 代理處理反應性。
  3. 更新 DOM:在檢測到實際更改後,框架需要更新 DOM。許多框架(如 React、Vue 和 Preact 等)使用虛擬 DOM(對時間優化),而 Angular 使用增量 DOM(對內存優化)。定義當代前端架構

早期 GUI 架構的範圍主要集中在代碼結構和組織上。它主要關注模型與其表示之間的關係,也就是視圖。那是當時的需求。

今天情況已經發生了變化。現代前端架構的需求已經遠不止簡單的代碼組織。在社區中,我們已經把這些知識傳承了下來。

大多數現代框架已經在組件的幫助下標準化了代碼結構和組織概念。它們是當今架構的基本單元。

還應注意,架構與其實現框架密切相關。這可以追溯到這樣一個事實,也就是 GUI 模式很複雜,並且由於它直接對接用戶而無法獨立於其實現來討論。我們可以繼續說一個框架就是它的架構。

設計良好的組件系統是任何前端架構或框架的最基本需求。除此之外,它必須解決前面討論的許多其他問題,如聲明式 DOM 抽象、顯式數據流、更改檢測等。大多數元素都在下圖中高亮顯示:

現代前端架構元素

考慮到所有這些因素,我們可能會想要創建一個參考架構,但這根本不可能。每個框架或庫在實現時都有自己的庫特性。例如,React 和 Vue 都支持單向數據流,但 React 更喜歡不可變數據,而 Vue 則支持可變性。因此,最好針對單個框架來全面了解其架構。

話雖如此,我們仍然可以嘗試創建近似的參考架構,如下所示:

現代參考架構

到目前為止我們描述的所有特徵或元素都包含在這個架構中。它乾淨地映射到三層架構,但第二層的中間件是可選的。第二層表示 UI 伺服器,理解的人不多。UI 伺服器只是一個伺服器,旨在為現代重客戶端的 UI 應用程式提供服務。它負責正交領域,如 SSO 集成、身份驗證和授權,服務端渲染、會話管理、服務 UI 資產和緩存等。此外,當大規模應用程式在某處部署 API 伺服器時,它充當反向代理以調用 API 避免 CORS 問題。這裡事實上的標準選擇是 Node.js,因為許多框架都在 Node 中編寫了 SSR 渲染器,而 Node 由於其異步特性而擅長處理大 I/O 請求。我們將在另一篇關於 Web 應用程式拓撲的文章中進一步討論 UI 伺服器。

當代架構最重要的變化是模型的概念。

模型不再被視為一個黑盒子。它被分隔成應用程式範圍的全局狀態和組件範圍的本地狀態。全局狀態通常使用複雜的狀態容器(如 Redux、Mobx 和 Vuex 等)管理。每個組件的本地狀態是三個事物的聯合——全局狀態切片、組件的私有本地狀態(異步數據、動畫數據和 UI 狀態等)和由父組件作為 props 傳遞的最終狀態。我們可以將本地狀態視為模型視圖模型的更好抽象。當我們將 GraphQL 添加到這個等式中時,狀態管理會發生變化。

數據從上到下,從父組件流向子組件時是單向的。雖然框架允許數據直接反方向流動,但不鼓勵這樣做。相反,事件是從子組件中觸發的。父組件可以監聽或忽略它們。

不完整的藍圖

這個參考架構並沒有真正捕捉到當代架構的全部本質。大多數 Web 流量由靜態網站和 CMS(內容管理系統)驅動。現代工具鏈已經大大改變了我們開發和部署這些應用程式的方式。在 CMS 的情況下,他們通過解耦前端與後端而變得 無頭(Headless)StrapiContentful 等的崛起就是明證。與此同時,我們不再使用純 HTML 和 CSS 構建靜態網站。靜態站點構建器和生成器也變得非常流行。我們現在可以使用相同的前端框架來構建由複雜的構建工具輔助的靜態網站。使用 React.js 時,我們可以使用 Gatsby.jsVue.js,我們有 Nuxt.js。當我們編譯代碼時,它會生成一個靜態網站,可以完整部署到任何靜態 Web 伺服器。這種技術稱為預渲染,與服務端渲染對應。

這裡我們有了另一個用於構建靜態網站的當代架構。這裡的思想是使用像 Strapi 一樣的無頭 CMS,並使用像 Gatsby 這樣的靜態網站構建器來構建前端。在構建中,當我們生成靜態網站時,我們從 CMS 中提取所有數據並生成頁面。

當作者更改無頭 CMS 中的內容時,我們重新觸發我們的靜態網站的構建,使用新內容構建網站並立即部署到伺服器或 CDN。

構建靜態網站——現代方式

這個新的工作流程就像運行一個成熟的動態網站一樣好用,也沒有 CMS 的缺點,如複雜的部署和緩慢的加載時間等等...... 由於靜態網站可以直接部署到 CDN。我們得以快速加載並改進緩存。我們還擺脫了靜態網站的所有問題,如更新周期緩慢和缺乏可復用性等。這裡引述 Nuxt.js 網站的願景——

我們可以進一步考慮使用 nuxt generate 並託管在 CDN 上的電子商務 Web 應用程式。每當產品缺貨或補充庫存時,我們都會重新生成 Web 應用程式。但如果用戶在此期間瀏覽這個 Web 應用程式,因為有了對電子商務 API 的 API 調用,應用將保持最新狀態。無需再用伺服器 + 緩存的多組實例!

總而言之,現代前端解決方案構建在基於組件的單向架構之上。

進一步考慮單向架構的話,可以通過多種方式實現它們。框架有自己的做事方式。一些不錯的例子包括:

  1. Flux(為 React 設計)。
  2. Redux(主要與 React 共用,但視圖不可知)。
  3. MVU——模型視圖更新(用於 Elm)。
  4. MVI——模型視圖意圖(用於 Cycle.js)。
  5. BEST、Vuex 和 Ngrx 等。

Andre Staltz 在他的博客文章中很好地描述了這些模式:AndréStaltz——單向用戶介面架構:

https://staltz.com/unidirectional-user-interface-architectures.html?source=post_page-----fb5b500b0231----------------------

當代還是現代???

到目前為止,我們有意不用「現代」這個詞,而一直在說的是「當代」。今天的架構實踐僅僅是我們舊有理念的進化。我們社區試圖將新事物融入現有的生態系統,總是留在邊界內,很少打破常規。因此,「當代」這個詞更能準確地描述這種理念。

在定義「當代」時,我們必須將所有鬆散的目標聯繫起來,必須將過去、現在和未來聯繫起來。我可以想到三種可能的連結——

  1. 過去——將今天的組件與歷史上的 MV* 相關聯
  2. 現在——帶有 Web 組件的場景
  3. 未來——函數組件將今天的組件與歷史上的 MV* 聯繫起來?

到這裡事情應該都很清楚,但可能會出現一個問題,那就是這些模式如何與之前的模式聯繫起來。組件不是 MVVMMV* 的更好實現嗎?

如前所述,對於當代架構而言這只是一個底層的問題。然而,當代模式是關於整個應用的推斷。它們處理的是更高級別的抽象。UI 組件是一個原子單元,它從父級接收全局狀態切片,將其與自己的本地狀態組合,並將輸出顯示給用戶。

單向模式可以解決更大的難題。它說的是在兄弟組件之間通信並維護應用程式範圍的狀態。如果組件允許垂直分工,這些模式會為整個應用程式帶回水平分工。

如果還是有些糊塗,請考慮 Vue.js 這個例子。Vue 組件是 MVVM 的完美實現,同時我們可以使用 Vuex(Vue 的單向狀態容器)來管理應用程式範圍的狀態。架構存在於多個抽象層次。

Web 組件的場景

組件架構幾乎是所有框架的基礎,人們正在嘗試將組件的概念標準化為官方 Web 標準,儘管它們背後的推斷方式完全不同。此外我將它們稱為嘗試,因為即使成為了標準,許多框架作者也擔憂其可行性。

在本文中,最重要的關注點是數據流。Tom Dale 很好地總結了這個問題:

根據我的經驗,與框架無關的組件還有很長的路要走。

關於其他問題需要看 Rich Harris 的博文《為什麼我不用 Web 組件》:

https://dev.to/richharris/why-i-don-t-use-web-components-2cia?source=post_page-----fb5b500b0231----------------------


這並不是說當我們定義自己的技術棧時應該完全避免它們。一般的建議是從按鈕、複選框和收音機等枝葉組件開始一步步慢慢來,總是要謹慎行事。

函數組件和 Hooks——這是啥?

當我們將組件當作 MVVM 的實現時,我們通常期望一個視圖模型對象,它的 props 和方法由視圖通過綁定使用。在 React、Vue 和 Angular 等情況下,它通常是類實例。但是這些框架還有函數組件(沒有任何本地狀態的組件)的概念,其中實例根本不存在。此外,React 最近引入了一種使用 Hooks 編寫組件的新方法,允許我們在沒有類語法的情況下編寫有狀態組件。

React Hooks——你注意到這裡缺少「this」指針了嗎?

這裡的問題是——視圖模型對象在哪裡?Hooks 的創意很簡單,但在跨調用維護本地狀態的理念上完全不一樣。但從架構的角度來看它仍然是之前的理念。我們可以將其視為簡單的語法級別更改。我們都知道 JavaScript 語言中的類很糟糕,經常令人困惑,讓開發人員很難編寫乾淨的代碼。Hooks 擺脫了類,從而解決了這個問題。

唯一改變的是視圖模型的概念。無論是帶有 Hooks 還是函數組件有狀態組件,我們都可以假設組件的視圖模型對象是它的詞法語境(Lexical Context)閉包。該組件接收的所有變量、Hooks 值或 props 共同形成其視圖模型。其他框架也採用了這一理念。

看起來函數組件就是未來趨勢。不過我不會說 Hooks 是一個功能齊全的長期解決方案(聽起來好奇怪),但在語法層面上它很優雅,並且可以緩解古老的類問題。如果你不認為語法很重要,請看Svelte:https://svelte.dev/。

當代架構的下一階段

與 Web 應用程式相關的每項新技術都會在某種程度上影響前端應用程式。目前有三種趨勢——GraphQL、SSR 和編譯器,這裡必須具體介紹一下才算完整。

GraphQL

GraphQL 是一種服務端查詢語言。你可能已經看過有人說它取代了 REST,但事實並非如此。當我們談論 REST 時,它是一種元模式。在概念層面,它採用面向資源的架構來定義應用程式域模型。在實現層面,它使用 HTTP 協議的語義來交換這些資源,以賦予 Web 共享信息的方式。

現代業務需求很複雜,許多工作流程不能簡單地作為 HTTP CRUD 類比的資源公開。這就是 REST 比較尷尬的地方。GraphQL 旨在消息傳遞級別替換 REST 的純 HTTP 協議。GraphQL 提供了自己的消息傳遞封裝,可以被 GraphQL 伺服器理解,並且還支持查詢服務端資源(域模型)。

但是 GraphQL 客戶端實現的副作用是,GraphQL 已經開始侵占狀態容器的職責。我們來看基本的事實,那就是客戶端的模型只是服務端模型的一個子集,前者專門針對 UI 操作標準化,那麼像 Redux/Flux 這樣的狀態容器只是在客戶端緩存數據而已。

GraphQL 客戶端內置了緩存支持,可跨多個請求來緩存。

這個簡單的事實讓開發人員可以省掉許多與狀態管理相關的樣板代碼。在宏觀層面,它的形態仍然有待觀察。以下帖子詳細描述了具體的機制:


GraphQL是怎樣取代Redux的。

https://hackernoon.com/how-graphql-replaces-redux-3fff8289221d?source=post_page-----fb5b500b0231----------------------


用React Apollo瘦身我們的Redux代碼。

https://blog.apollographql.com/reducing-our-redux-code-with-react-apollo-5091b9de9c2a?source=post_page-----fb5b500b0231----------------------


一定要探索 GraphQL,因為它是未來趨勢。


SSR——服務端渲染

過去服務端 MVC 是非常重要的,伺服器會生成靜態或動態 HTML 頁面,這些頁面很容易被搜尋引擎抓取。由於客戶端框架可以提供出色的用戶體驗,我們正逐漸在瀏覽器上渲染所有內容。完全客戶端渲染的應用程式在請求伺服器時返回的典型 HTML 頁面幾乎是一個空頁面:

SPA 應用程式的初始 HTML 文件——幾乎是空的!

一般來說這沒什麼問題,但在構建電子商務網站時遇到了麻煩,因為這類網站需要較快的加載速度和對 SEO 友好的可抓取內容。麻煩還不止於此,行動電話的網際網路連接速度也很慢,而一些入門級設備的硬體也很差。搜尋引擎對完全客戶端渲染的應用程式抓取的能力有限。

為了緩解這個問題,老式的 SSR——服務端渲染 又回來了。有了 SSR,我們可以在伺服器上渲染客戶端 SPA,合併狀態,然後將完整渲染的頁面發送到客戶端。它減少了應用程式頁面的初始加載時間,從而提高了網站的響應速度。

SSR 是下一步進化,它填補了客戶端和服務端之間的鴻溝。

由於客戶端代碼是 JavaScript,我們需要服務端的等效引擎來執行 JS 代碼。Node.js 作為 JavaScript 引擎是執行 SSR 的服務端技術棧的事實標準。雖然 SSR 設置可能變得非常醜陋和複雜,但許多流行的框架已經提供了一流的工具和更高級別的框架,為 SSR 提供了非常流暢的開發人員體驗。

SSR 徹底改變了我們的日常開發工作流程。我們比客戶端——服務端抽象更進一步。它引入了強制的三層架構,其中基於 Node.js 的伺服器是關鍵的中間件。但從架構的角度來看——在數據流和分工層面所有這些都是相同的。SSR 既不引入新的數據流,也沒有改變已有的存在。

編譯器時代:Svelte——編譯器還是縮小的框架?

我不知道該如何描述 Svelte才好。我能說的是——當用戶啟動 Web 應用程式時,框架就在瀏覽器中運行。框架提供的轉換抽象有其運行時成本。Svelte 是不一樣的。

與靜態站點構建器一樣,Svelte 在構建時運行,將組件轉換為高效的命令式代碼,從外部更新 DOM。

所以 Svelte 是一個基於組件的框架,它展示了當代前端框架的所有特性,但同時它也是一個編譯器。編譯器將原始碼編譯為高性能的命令式 JavaScript 代碼。作為一個編譯器,它可以做許多其他框架不能做的事情:

  1. 在構建時提供可訪問性警告。
  2. 實現代碼級優化。
  3. 生成較小的包。
  4. 在不破壞語法的前提下將 DSL 整合到 JavaScript 中。

其宗旨是編寫更少的代碼。Svelte 證明編譯器可以實現許多以前用純 JavaScript 無法實現的功能。如果 Svelte 還不夠,那麼我們可以用 Stencil.js,這是一個用於編寫 Web 組件TypeScript+JSX 編譯器。

其中,一些想法已經成為某種形式的主流思想——Angular AOT 編譯器和 Vue 單文件組件等等。然後還有其他人將這種思想推向極致:

http://imba.io/?source=post_page-----fb5b500b0231----------------------


Rich Harris 的這篇演講很好地展示了 Svelte 的底層哲學,並與 React 做了主觀對比:

https://docs.google.com/presentation/d/1PUvpXMBEDS45rd0wHu6tF3j_8wmGC6cOLtOw2hzU-mw/mobilepresent?slide=id.p

同樣,編譯器的前端開發前景也很光明。

還有其他方法!偉大的單體!

雖然完整的客戶端框架現在風靡一時,但它並不是唯一的行事方式。Web 依舊是多樣化的。仍然有許多應用程式是服務端驅動的,而且將繼續這樣做。

但這是否意味著它們的體驗會很差?當然不是!架構的設計目標是支持產品,Basecamp 團隊開發的框架 Stimulus就做得很好。要了解他們的理念可以查閱:

https://m.signalvnoise.com/the-majestic-monolith/?source=post_page-----fb5b500b0231----------------------

它是一個適度的框架,通過輕量級 JavaScript 提升後端渲染頁面的交互性,同時採用最新實踐和最新標準。Stimulus 通常與 Turbolinks並用,以創建一流的 SPA 用戶體驗。(我是 Basecamp 的老用戶了,發現它比其他許多 SPA 應用程式都更精緻。)

Stimulus 在某種意義上是不一樣的,因為它是通過 HTML 而非 JavaScript 驅動。狀態在 HTML 而非 JavaScript 對象中維護。數據流非常簡單:控制器附加到 DOM,它公開了可以附加到操作上的方法,從而執行進一步的操作。

你可能會想到,它很像 Backbone 和 Knockout 的時代——確實如此。目標很簡單——為後端驅動 Web 提供的交互式前端框架。唯一的不同是 Stimulus 採用了現代社區標準和實踐。

Strudel.js是另一個類似理念的適度框架。在 2019 年,我們可以使用像 RE:DOM 這樣的當代 DOM 庫。


雖然它們可能無法解決當代前端框架面臨的所有問題,但它們給 JavaScript 審美疲勞的世界帶來了一絲喘息之機。

總結

只有一個詞能用來描述 GUI 架構——華麗。雖然對於前端軟體開發來說,MVC 作為一種模式已經逝去了,但原則是永恆不變的。

我們從原始的 MVC 開始探索了著名的桌面模式。然後我們轉到 Web 應用程式並使用相同的原則來得到了流行的模式。之後我們轉向早期的獨立客戶端模式,最後全面討論了 SPA 框架。

重要的一點是,今天的前端框架是面向組件的,它們將 MVC/MVVM 關注點作為一個層面,同時要處理新的環境和挑戰。

最後,我們瀏覽了一遍前端架構的新面孔,包括 JavaScript 驅動的 SSR 和 GraphQL 的崛起。同時我們跳過了許多令人興奮的新技術,如 HTTP2 和 WebAssembly 等,它們可以改變前端架構的未來,改變我們對應用程式的看法。

由於所有這些模式涉及的術語都有所重疊,並且通常是由社區各自獨立開發的,因此很難根據進化時間來定義明確的線性時間軸。有時將不同的模式聯繫在一起是有問題的。此外為了簡單起見,我們可以自由地描述某些概念,不用特別研究它們的細節。沒有現成的命名法則可用,所以有些理念我用了自己發明的術語。

Web 應用程式拓撲是 Web 應用程式架構的另一個關係密切的領域。拓撲通常在技術棧選擇、安全約束和性能等方面對前端開發產生深遠影響。因此這將是下一篇文章的主題。

我們希望本文能夠幫助你更好地理解前端架構的元素。求分享擴散!

英文原文: https://blog.webf.zone/contemporary-front-end-architectures-fb5b500b0231

關鍵字: