每日最新頭條.有趣資訊

幾行代碼就能完成 Web 組件的數據綁定

作者丨Danny Moerkerke

譯者丨王強

策劃丨王瑩

這不是什麽難事,一般來說沒必要動用虛擬 DOM。

今年早些時候我寫了一篇文章,聲稱 Web 組件最終將取代前端框架。

這篇文章引起了很多爭議,這大大出乎我的意料,但也讓我收獲良多。有很多人同意我的觀點,也有很多人持否定態度,甚至有人覺得我根本就是腦子進水,應該永遠禁止我再寫代碼了。總的來說,爭論的雙方都提出了很不錯的觀點。

批評聲音主要指出現有的框架提供了一種通過數據綁定編寫視圖的聲明式途徑,這是原生 Web 組件天生不具備的能力。這一觀點本身沒錯,但其實 Web 組件是很容易實現數據綁定的,我將在本文中演示具體做法。

聲明式數據綁定的情況

數據綁定最早是被 Angular、Backbone 和 Ember 等框架推廣而流行開來的,現在則在某種程度上是編寫視圖的標準途徑。它能讓“視圖作為數據的函數”,意味著每當某些數據發生變化時,相關視圖將“自動”更新。

不需要冗長的 DOM 操作來保持數據和視圖同步,只需更新數據,視圖就會隨之變化。這是一項殺手級功能,如今但凡理智的開發人員就會用它。所以很容易理解為什麽開發人員會使用提供了數據綁定功能的框架,就算框架對於應用來說太大材小用也無所謂:既然框架打理好了一切,何必要費心費力處理那些麻煩的 DOM 操作呢?

但數據綁定並不是什麽魔法,你用不著為了用它而動用整個框架。在 Web 組件裡只需要幾行代碼就能輕鬆搞定數據綁定了,沒什麽特殊的。

如何實現

就像我上面說的那樣,數據綁定並沒有那麽神奇。當基礎數據發生變化時,你的視圖不是憑空“神奇”地更新的。在框架深處,不為人知的某個角落裡的設置代碼負責在數據更改時調用並更新視圖。

AngularJS 使用了所謂的“摘要循環”:這是一種粗暴的檢查機制,不斷檢查哪些數據已更改,以便隨時更新對應的視圖。

React 面世時提供了另一種據稱性能更好的解決方案,稱為虛擬 DOM:它是一種 DOM 的 JavaScript 表示,隻更新已更改的 DOM 部分。這對列表來說很合適——列表的少數項目發生變化時無需重新渲染整個列表,只需更新已更改的項目。

哪種工具最合適?

圖源:https://xkcd.com/974/

這對於具有複雜 UI 的較大應用程序來說非常有用,但對於大多數應用程序來說實在有些殺雞用牛刀了。編寫一些監視數據的代碼並在數據發生變化時更新相關視圖並不是什麽難事。問題是這些數據通常需要傳遞給也需要數據綁定的子組件,所以最後往往會有很多 DOM 操作。

你需要的是一種在數據被推送到子組件時觸發子組件中相同的數據綁定操作的方法。只要父組件的數據發生更改並且某些數據綁定到子組件的視圖,該子組件的視圖也需要更新。

一種方法是利用所有組件的基類,因為 Web 組件是使用 JavaScript 類創建的,所以這是一個很好的選擇。默認情況下 Web 組件擴展 HTMLElement,但我們也可以創建自己的基類來擴展 HTMLElement,如下所示:

然後我們創建的每個 Web 組件都擴展了這個 CustomElement 基類:

如果你想直接查看代碼,可以訪問 Github 鏈接。

我們將 CustomElement 的內部 state 屬性綁定到視圖來實現數據綁定:

我的第一個想法是將 this.state 實現為 Proxy,這樣 state 對象的任何突變都將被自動攔截;但由於 Proxy 可能會影響性能,因此我決定實現一個 setter,它還能同時設置多個屬性:

setState 方法遍歷 newState 對象的所有條目,並將所有值設置為 this.state 上的對應屬性,隨後我們就應該使用這些值來更新視圖。

通過標準 data 屬性將值綁定到視圖上,在本例中為 data-bind:

這種綁定可以達到任意深度,所以下面這種情況也能做到:

還可以將數據綁定到 Web 組件的特定屬性。在此示例中,數據綁定到的 title 屬性上:

視圖的更新實際是在 CustomElement 中的 updateBindings 方法中實現的。通過 setState 方法更新 state 時,它會解析更新的屬性以查找綁定到這些屬性的 HTML 元素。

例如下面這樣:

如果綁定數據的元素是常規 HTML 元素,那麽將簡單地更新其 textContent。在這兩種情況下,只需幾行代碼即可有效實現 DOM 的更新。

列表該怎麽處理?

像虛擬 DOM 這樣的解決方案真正的用武之地是渲染列表。例如隻更改列表的一部分時,虛擬 DOM 將僅更新已更改的部分,而不是重新渲染整個列表。

其機制是在第一次渲染列表時創建 DOM 節點,然後在列表更改時隻更新這些現有節點(textContent、屬性等)。複用已經創建的節點比重新渲染整個列表重建所有節點的開銷小得多,因此對於非常大的列表來說這種方法效率更高。

但如果你的列表平均只有 25 個項目時,你可能會想知道重新渲染整個列表能比虛擬 DOM 的方法慢多少。當你渲染 250 個項目時可能會變得很慢,但理智的開發人員在這種情況下就應該分頁了。

我不是說大家就應該拋棄虛擬 DOM,因為它的確是很棒的技術。如果遇到真正需要虛擬 DOM 的情況,自然一定要使用它。我只是希望大家遇到比較輕量的問題時先思考一下能不能找到一個比較輕量的解決方案,而不是上來就動用重量級的殺手鐧。

customElement 演示的 Github 倉庫包含一個 Web 組件,它在 li 標簽內渲染任何設置為其 items 屬性的字元串陣列。只要將 items 設置為新陣列它就會重新渲染整個列表,但是複用現有的 li 標記也很簡單。

結論

答案很清楚了,只需幾行代碼即可對 Web 組件進行聲明式數據綁定。我覺得自己已經清楚地證明了數據綁定很容易實現,並且你不需要動用整個框架也能使用它。

上面提到的 Github 倉庫中的代碼不是 React 或 Vue.js 等框架的替代品,本來也沒這個意思。框架提供的並不只有數據綁定,本文和涉及的代碼是為了證明你不一定需要一個框架來實現聲明式數據綁定。

除了數據綁定之外,customElement 還提供了一些方便的方法來選擇元素以及顯示和隱藏元素。

我請大家好好看看我的代碼,仔細研究一下,能給我反饋的話我會感激不盡!

https://medium.com/swlh/https-medium-com-drmoerkerke-data-binding-for-web-components-in-just-a-few-lines-of-code-33f0a46943b3

點個在看少個 bug

獲得更多的PTT最新消息
按讚加入粉絲團