元件化的Web王國

埃姆傑發表於2014-08-28

內容提要

使用許多獨立元件構建應用程式的想法並不新鮮。Web Component的出現,是重新回顧基於元件的應用程式開發模式的好時機。我們可以從這個過程中受益,瞭解如何使用現有技術完成目標,並且在未來做出自己的前端Web應用。

什麼是元件?

軟體開發是一個語義豐富(術語通常不止一個意思)的領域。很顯然,這裡的“元件”是一個很泛的稱呼,所以有必要指明我們想要表達的,在前端Web應用的語言環境中的意思。

前端Web應用中的元件,是指一些設計為通用性的,用來構建較大型應用程式的軟體,這些元件有多種表現形式。它可以是有UI(使用者介面)的,也可以是作為 “服務”的純邏輯程式碼。

因為有視覺上的表現形式,UI元件更容易理解。UI元件簡單的例子包括按鈕、輸入框和文字域。不論是漢堡包狀的選單按鈕(無論你是否喜歡)、標籤頁、日曆、選項選單或者所見即所得的富文字編輯器則是一些更加高階的例子。

提供服務型別的元件可能會讓人難以理解,這種型別的例子包括跨瀏覽器的AJAX支援,日誌記錄或者提供某種資料持久化的功能。

基於元件開發,最重要的就是元件可以用來構成其他元件,而富文字編輯器就是個很好的例子。它是由按鈕、下拉選單和一些視覺化元件等組成。另一個例子是HTML5上的video元素。它同樣包含按鈕,也同時含有一個能從視訊資料流渲染內容的元素。

為什麼要構建元件?

既然現在已經明白元件的意思,就看看使用元件的方法構建前端應用的好處。

模組

你可能聽說過 “元件是天然模組”的說法。好吧,感謝它,我們又要解釋這裡的術語!

你可能會覺得“元件”的說法更加適合用來描述UI,而“模組”更適合描述提供服務的功能邏輯。而對於我來說,模組和元件意思相近,都提供組織、聚焦和封裝,是與某個功能單位相關的。

高內聚

又是一個軟體工程的高頻詞! 我們將相關的一些功能組織在一起,把一切封裝起來,而在元件的例子中,就可能是相關的功能邏輯和靜態資源:JavaScript、HTML、CSS以及影像等。這就是我們所說的內聚。

這種做法將讓元件更容易維護,並且這麼做之後,元件的可靠性也將提高。同時,它也能讓元件的功能明確,增大元件重用的可能性。

可重用

你看到的示例元件,尤其是Web Component,更關心可重用的問題。功能明確,實現清晰,API易於理解。自然就能促進元件複用。通過構建可重用元件,我們不僅保持了 DRY(不要重複造輪子)原則,還得到了相應的好處。

這裡要提醒: 不要過分嘗試構建可重用元件。你更應該關注應用程式上所需要的那些特定部分。如果之後相應需求出現,或者元件的確到了可重用的地步,就花一點額外時間讓元件重用。事實上,開發者都喜歡去創造可重用功能塊(庫、元件、模組、外掛等),做得太早將會讓你後來痛苦不堪。所以,吸取基於元件開發的其他好處,並且接受不是所有元件都能重用的事實。

可互換

一個功能明確好元件的API能讓人輕易地更改其內部的功能實現。要是程式內部的元件是鬆耦合的,那事實上可以用一個元件輕易地替換另一個元件,只要遵循相同的 API/介面/約定

假如你使用GoInstant提供的實時功能服務元件,那他們這周關閉服務這樣的新聞會影響到你。然而,只要提供了相同的資料同步API,你也可以自行構建使用一個 FirebaseComponent 元件或者 PubNubComponent 元件。

可組合

之前也討論過,基於元件的架構讓元件組合成新元件更加容易。這樣的設計讓元件更加專注,也讓其他元件中構建和暴露的功能更好利用。

不論是給程式新增功能,還是用來製作完整的程式,更加複雜的功能也能如法炮製。這就是這種方法的主要好處。

是否有必要把所有的東西轉換成元件,事實上取決於你自己。沒有任何理由讓你的程式由 你自己 的元件組合成你最驚歎的功能 ,乃至 最花哨的功能。而這些元件又反過來構成其他元件。如果你從這個方法中得到了好處,就想方設法地去堅持它。然而要注意的是,不要用同樣的方法把事情變得複雜,你並不需要過分關注如何讓元件重用。而是要關注呈現程式的功能。

現在就開始構建元件

在 Caplin Systems 構建基於元件的自有應用程式時,我用到了幾條原則和實踐。這些原則由 BladeRunnerJS(BRJS) 開源工具集支撐。它被稱作”BladeRunnerJS” 是因為我們將程式功能都封裝在稱作 Blades 的東西中。Blade是可以在某個應用中重用的功能特性,但是不可以在程式間重用。當功能 真的 變得更加通用的時候,我們將相應的定義移到庫檔案中,供各個程式間使用。特定應用中的元件(blade)和我們程式間的通用元件可以使用,我們只要找到最好滿足需求的任何庫和框架。

那麼,現在什麼庫和框架能夠幫助我們構建元件呢?

在決定構建應用時應使用何種技術時,只需要看看流行的 TodoMVC 網站就可以看到大量可供選擇的前端庫和框架。你也許會覺得任何一種方案都能用來構建基於元件的應用程式。然而,他們之中的一些方案內建了對元件的支援。其中比較有名的是AngularJS、Ember 和 React。

元件間是如何通訊的?

在深入示例之前有必要簡單地提到元件間通訊的問題。如果元件之間是“獨立”、“模組化”的,他們又是如何相互通訊的呢?

最顯而易見的答案就是讓元件間相互引用並通過他們之間的API互動。這樣做的問題就在於,這種做法會讓元件相互依賴。短期內可能還好,一段時間以後,你在修改程式的時候程式會失控,修改一個元件就會對另一個元件產生極大的影響。決定移除一個不能帶來預期價值元件可能會讓你的應用程式停止工作,因為它背後會有數個元件依賴於它。

此時,解決方案是提供鬆耦合的,讓元件之間很少或者幾乎不知道彼此的方案。元件並不直接建立其他元件,在他們需要通訊的時候,他們通過“介面/約定”或者通過 服務。我們在構建BRJS程式時考慮了很多這些方面的東西,並且使用 ServiceRegistry 訪問用於元件間通訊的服務或者是Web API這樣的資源。Angular和Ember採用了服務和依賴注入解決這類問題。

示例元件my-avatar

為了展示我們如何用這些庫和框架構建最基本的元件,我們建立了一個帶有UI,用於取回和顯示使用者頭像的簡單示例。在可能的情況下,該元件會有 my-avatar 標籤,會從以下兩個屬性中取得頭像:

  • service 允許設定一個服務。例如 twitter 或者 facebook
  • username 用於取回該使用者名稱相對應的頭像

AngularJS

AngularJS 可能是現在用於構建程式最流行的前端解決方案了。作為建立者的Google,重新思考HTML,考慮如何重新發明,滿足如今Web開發的需要。

Angular中可以使用自定義指令定義元件。之後,你可以使用 HTML 標記宣告自定義元件。

檢視程式碼演示: http://jsbin.com/lacog/2/edit

這個例子展示了使用Angular指令的簡單程度。值scope 定義了從  my-avatar 元素中取得,並且之後用來構建相應的img標籤和渲染成使用者頭像的屬性。

Ember

框架與庫的爭論曠日持久,總的來說框架是強制你按某種方式做事情,所以它是邪惡的。很顯然,Angular是個框架,而Ember的作者,Yehuda Katz和Tom Dale也很樂意把Ember看作框架

Ember 有對它稱作元件的內建支援。Ember Components背後的理念是儘可能的向Web Components看齊,當瀏覽器支援允許時,就可以很方便地遷移到Web Components中。

檢視程式碼演示: http://jsbin.com/nawuwi/4/edit

上面的例子中使用了 handlebars 做模板,所以元素的定義不是同一種語法。

React

React 雖然是個新人,但是卻已經有很多的追隨者。它由Facebook開發,並且已經全面用於Instagram的UI和部分Facebook的UI。

使用React構建元件的推薦方式是使用叫做 JSX 的東西來定義它們。這是一種“推薦在React上使用的JavaScript語法轉換”。請不要因此分心。他們已經在文件中指出,這個想法就是用來幫助你在JavaScript中寫出HTML標記的。

我不是說你並不可以直接在HTML中新增標籤,而必須使用JSX建立自己的元件。但是,只要你定義了一個元件,你就可以使用這個元件創造其他元件。

檢視程式碼演示: http://jsbin.com/qigoz/5/edit

因此,元件使用的宣告語法需要相應的HTML元素和對 React.RenderComponent 的呼叫。

未來:Web Component和其他

Web Component才是未來!正如名字所表示的那樣,他們承諾將帶來可以將功能封裝成元件的瀏覽器原生支援。

我將簡單展示Web Component並且演示我們現在可以如何使用它。更加深入的內容請參考本文末尾的 “外部資源” 一節。

他們提供的功能包括:

自定義元素

我們在上面關注的是用Angular、Ember和React構建 my-avatar 的例子。可能的情況下,這樣的方式將以頁面上或者模板上新增的自定義元素表示。Web Component包括通過自定義元素獲得的原生支援 – 絕對是Web Component標準的基本組成部分。

定義新元素,包括訪問元素生命週期的部分事件例如何時建立(createdCallback)、何時新增在DOM樹上(attachedCallback)、何時從DOM樹上分離(detachedCallback),何時元素屬性改變(attributeChangedCallback(attrName, oldVal, newVal))。

自定義元素的一個重要的部分就是有能力從原有元素擴充套件,因而得到原有元素相應的功能。示例中我們擴充套件了 <img>元素 。

最終,我們所寫的程式碼中,自定義元素正在並且傾向去做的就是將複雜的東西抽象化,讓使用者關注於單個元件產生的價值,從而用來構建更加豐富的功能。

Shadow DOM

還記得iframe們嗎?我們還在使用它們,是因為他們能確保元件和控制元件的JavaScript和CSS不會影響頁面。 Shadow DOM 也能提供這樣的保護,並且沒有iframe帶來的負擔。正式的說法是:

Shadow DOM的設計是在shadow根下隱藏DOM子樹從而提供封裝機制。它提供了建立和保障DOM樹之間的功能界限,以及給這些樹提供互動的功能,從而在DOM樹上提供了更好的功能封裝。

HTML匯入

我們長時間以前就可以匯入JavaScript和CSS了。 HTML匯入功能提供了從其他HTML文件中匯入和重用HTML文件的能力。這種簡單性同時意味著可以很方便地用一些元件構建另一些元件。

最後,這樣的格式很理想,適合可重用元件,並且可以用你最喜歡的包管理解決方案發布(例如: bower、 npm 或者 Component)。

模板

我們中的許多人已經使用像handlebars、mustache或者underscore.js中的模板這樣的解決方案(就像我們在上面的Ember示例中用的一樣)。Web Component通過 template元素 提供了模板的原生支援

原生模板讓你可以宣告分類為“隱藏DOM”但是解析成HTML的標記片段。他們在頁面載入時沒有用處,但是可以在執行時例項化。他們可以 被檢索到 ,但是在插入活動的DOM樹前不會載入任何相關資源。

Platform.js

但是,就像每次提到新特性一樣,我們不能確定瀏覽器是否支援這些特性。

截至2014年6月27日,Web Component 的瀏覽器支援情況

同樣,我們也能通過一些神奇的相容程式碼,開始使用某些Web Component所提供的功能。

有了相容庫的Web Component支援情況

好訊息是兩個最先進的瀏覽器廠商Google和Mozilla正在努力完善相容庫 ,幫助我們使用Web Component。

以下示例展示使用platform.js後我們可以如何定義作為img元素擴充套件的my-avatar元素。最棒的是它能用到原生img元素的所有功能。

檢視程式碼演示: http://jsbin.com/pihuz/4/edit

點選 HTML5 Rocks Custom Elements tutorial 以檢視建立自定義元素的更多資訊。

注:如果你對platform.js感興趣,也可以看看 bosonic

原生技術的支援目的就是給我們提供相應的構建基礎。所以Web Component並不是庫和框架的末日訊號。

Polymer

Polymer 是演示構建基於原生Web Component功能的最佳示例。它提供了精選的機制用來建立自定義的Polymer元素,並且提供了許多核心的UI元件,讓你可以建立自己的應用程式。

下面你可以看到 my-avatar 元素的簡單建立過程,同時我們也得到了想要的標記。

檢視程式碼演示: http://jsbin.com/gukoku/2/edit

Google正在大力推動Polymer。請檢視 Polymer getting started guide 檢視更多示例。

X-Tag和Brick

Mozilla開發了自己的自定義元素 相容庫,叫做 X-Tag。X-Tag是一個為啟用Web Component進行多項相容的庫,並即將提供對Web Component的完整支援。

以下就是使用X-Tag的 my-avatar 自定義元件,與標準文件十分類似:

檢視程式碼演示:http://jsbin.com/wexiz/2/edit

Mozilla同時還創造了一個叫 Brick 的庫,其中包括X-Tag,提供“一組用來方便快速構建Web應用程式的UI元件”,使用與Google的Polymer相似的方式。

總結

使用基於元件的架構構建應用程式有諸多好處,你能從現有的框架中學到,也能在構建前端Web應用程式時從推薦的Web Component中學習到。

這場元件化Web王國的旅程,讓我們在面臨框架和工具的選擇時猶豫不決。但是,Web Component會是最後的明燈!

Web Component會提供構建應用程式的原生統一的方法。現有的框架很有可能會轉而使用Web Component或者說明如何與它一同使用。Ember的策略是讓遷移到Web Component更加方便,而Facebook的React則是演示整合的好例子,已經有一個 ReactiveElements 演示它了。因為Angular和Polymer都是Google的專案,他們很有可能會走到一起。

外部資源(英文)

 

相關文章