原生js數據綁定

原生js數據綁定

雙向數據綁定是非常重要的特性—— 將JS模型與HTML視圖對應,能減少模板編譯時間同時提高用戶體驗。我們將學習在不使用框架的情況下,使用原生JS實現雙向綁定—— 一種為Object.observe_(譯註:現已廢棄,作者寫博客時為14年11月),另一種為覆蓋get/ set。PS:第二種更好,詳情請參閱底部的TL;DR(譯註:toolong;don’tread. 直譯為“太長,不想看”,意譯為“簡單粗暴來吧”)_。

1:Object.observe 和DOM.onChange

Object.observe()是一種新特性,其在ES7中實現,但在最新的Chrome中已可用—— 允許對JS對象進行響應式更新。簡單說就是—— 只要對象(的屬性)發生變化就調用回調函數。

一般用法為:

原生js數據綁定

這很方便,且能實現響應式編程—— 保證所有內容都是最新的。

如下:

原生js數據綁定

JSFiddle

如上,我們自己實現了模型到數據的綁定!封裝一下(譯註:此處原文為Let’sDRY ourselves with a helper function.DRY即don’trepeat yourself):

原生js數據綁定

JSFiddle

換一種方式—— 將DOM元素與JS值綁定起來。簡單的方法是使用jQuery.change

原生js數據綁定

JSFiddle

簡直不要太方便,在實際開發時,可以將兩者結合,通過函數來創建一個雙向數據綁定:

原生js數據綁定

注意:在雙向綁定時,需正確進行DOM操作,因為不同的DOM元素(input,div,textarea,select)有不同的取值方式(text,val)。同時注意:雙向數據綁定並不是必須的—— “輸出型”元素一般不需要視圖到模型的綁定,而“輸入型”元素一般不需要模型到視圖的綁定。

下面為第二種方式:

2:深入’get’和’set’屬性

上面的解決方法並不完美。比如直接的修改並不會自動觸發jQuery的“change”事件—— 例如,直接通過代碼對DOM進行修改,比如以下代碼不起作用:

原生js數據綁定

現在,我們來用一種更激進的方式實現——重寫getter和setter。因為我們不僅要監測變化,我們將重寫JS最底層的功能,即get/setting變量的能力,所以不那麼“安全”。後面我們將會看到,這種元編程的方式有多強大。

那麼,如果我們可以重寫get和set對象值的方法會怎麼樣呢?這也是數據綁定的實質。用Object.defineProperty()即可實現.

其實,以前就有已廢棄且非標準實現方式,但通過Object.defineProperty的實現方式更好(最重要的是標準),如下所示:

原生js數據綁定

現在user.name是nameValue的別名。但可做的不僅僅是創建新的變量名-我們可以通過它來保證模型和視圖的一致。如下:

原生js數據綁定

user.name現在綁定到#foo元素。這種底層的方式非常簡潔—— 通過定義(或擴展)變量屬性的get/ set實現。由於實現非常簡潔,因此可以根據情況輕鬆擴展/修改代碼—— 僅綁定或擴展get/ set中的一個,比如綁定其他數據類型。

可封裝如下:

原生js數據綁定

使用:

原生js數據綁定

JSFiddle

注意:上面的domElem.value只對input元素有效。(可在bindModelInput中擴展,對不同的DOM類型使用對應的方法來設置它的值)。

思考:

  • DefineProperty瀏覽器兼容性良好。
  • 注意:上面的實現中,在某些場景下,視圖可認為是符合SPOT(single point of truth)原則的,但該原則常常被忽視(因為雙向數據綁定也就意味著等價)。然而,深究下去可能就會發現問題了,在實際開發中也會遇到。——比如,當刪除DOM元素時,關聯的模型會自動註銷麼?答案是不會。bindModelInput函數在domElem元素上創建了一個閉包,使DOM元素常駐在內存中—— 並保持模型與模型的綁定關係 ——即使DOM元素被移除。即使視圖被移除了,但模型依舊存在。反之一樣—— 若模型被移除了,視圖依然能夠正常顯示。在某些刷新視圖和模型無效的情況下,理解這些內部原理就能找到原因了。

(譯註:SPOT簡單翻譯為“單點原則”,即引起變化最好的是由單一入口引起的,而不是由多個入口引起的,比如一個函數,其返回結果最好僅由參數決定,這樣輸入和輸出才能一致,而不會由於其他變化導致用一個輸入會出現不同的輸出)

這種自己實現的數據綁定方法與Knockout或Angular等框架的數據綁定相比,有一些優點,例如:

  • 理解:一旦掌握數據綁定的源碼,不僅理解更深入,而且也能對其進行擴展和修改。
  • 性能:不要將所有東西都綁定在一起,只綁定所需的,避免監測過多對象
  • 避免鎖定:若所用的框架不支持數據綁定,則自行實現的數據綁定更強大

缺點是由於不是真正的綁定(沒有髒檢查),有些情況會失敗——視圖更新時不會觸發模型中的數據,所以當試著同步視圖中的兩個DOM元素時將會失敗。也就是說,將兩個元素綁定到同一個模型上時,只有更新模型,則兩個元素才會被正確更新。可以通過自定義一個更新函數來實現:

原生js數據綁定

TL;DR:

當需要使用原生JS創建模型和視圖的雙向數據綁定時,如下:

原生js數據綁定

原生js數據綁定


分享到:


相關文章: