扯扯“Model Driven UI”

發表於2016-02-03

為什麼我認為對於構建應用程式而言,MVVM/React是比jQuery更容易的方式?

文章比較淺,科普性質,大神們別嫌棄。

“傳統”方式

用一種“傳統”的思路,我們要更新頁面某一個部分的UI,應該這麼做:

這個例子應該是一個典型的場景

  • 拉資料
  • 找元素
  • 改屬性

為什麼核心在於“找元素”呢?由於要儘可能的優化UI的效能,只能做最小更新操作,那麼就需要找到發生變化的那個欄位所需要的元素,單獨對其進行操作。

所以jQuery的核心就在於query,首當其衝就是它能最快捷的幫我們query出需要的元素來,很好的滿足了一個JS庫的核心需求。當然它的另一個優勢就是它的API設計得太簡便了,簡直是不會JS都能用,入門成本之低令人髮指。

這麼做的問題

一句話

UI被設計為依賴Model,Model不應該依賴UI。

如果實現成貧血Model層,就會在邏輯程式碼裡面去進行上面的query-update操作,如果是充血Model層那可能就在Model裡。不論怎樣,這樣做都違背了上述依賴關係。

很簡單,當UI發生變化(這種變化在迭代當中非常頻繁)的時候,不僅需要修改UI本身,也需要去修改邏輯程式碼或者Model層,比方說#name這個ID換掉了,得換個選擇器;比方說span變成了textbox,得把.html()換成.val();比方說整個UI層重新換了一套CSS命名規範,或者上了一個className混淆方案,可能讓所有的addClass/removeClass/hasClass全瞎;比方說運營需要“重要的事情說三遍”於是同一個欄位要被連續展現3次;比方說相簿改版,啥沒變,惟獨從井字格變成輪播圖了……

這些本身應該是UI的事兒——毫無業務邏輯在裡面——卻需要去改邏輯程式碼,依賴關係顛倒過來了,形成了anti-pattern。

所以現在流行說“單向資料流”,它是對上面所說的依賴關係的一個形象描述。

Model Driven UI

這概念誰說的來著,好像是Polymer。其實在12年的某個專案裡,我就在嘗試這個方式,當然,舉步維艱。

一個很糙的方式

當時的主要矛盾是,我們也實現了單向資料流,所有UI操作都呼叫Business層(相當於Controller)的介面,UI保持對Model的嚴格只讀。但Business層修改完了Model之後,下一步就非常難了,為啥難呢?因為“Model變了,Drive不起UI來”

如果Model只有一個簡單粗暴的change事件,那麼UI就倒了八輩子的大黴了,它根本不知道到底變了什麼,沒法做最小的UI更新,那麼效能上基本先Say Goodbye了。

於是實踐上的問題就來了,Business層在修改Model的時候需要如履薄冰地觸發一個“合理地小”的事件——不能太大,這樣UI大面積做無用的更新;不能太碎,這樣UI還需要做一個batch更新機制。
這樣的結果肯定就是事件的種類會隨著use case增多而大幅度增多,而可怕的就是UI必須對這些新增的事件一一作出響應,哪怕它跟之前某一個事件差別相當之小。

這當中自然也就隱含了Model對UI的間接依賴,邏輯程式碼需要對UI有比較深入的瞭解,才會知道怎樣去觸發一個事件它才會“合理地小”。

有了batch update,可以把Model的change做到欄位級別的CRUD事件了,但UI需要關心的事件就會呈一個數量級的增加。等於原本在邏輯程式碼裡集中更新UI,變為了在UI裡(藉助batch update)分散更新——事兒沒變少,就是換了個人在幹。

至少是解決了一個依賴倒置的問題,UI通過欄位來訪問Model,通過事件來訂閱更新自己,而Model則幾乎不會對UI產生直接依賴了,極端一些,Model對於UI是不是DOM都可以不關心了。

沒那麼糙的方式

現在有了MVVM和Virtual-DOM了,batch update也都是標配,Business層可以肆無忌憚的對Model進行任何粒度的CRUD。UI也不需要監聽Model上的各種事件了——簡單的說來,雖然整個資料流沒有變,但是每一個環節都變簡單了。

所以MVVM和Virtual-DOM解決的問題是資料繫結/資料展現嗎?是,也不全是。更深究地說,它們解決的問題是幫助UI和Model之間“髒活累活誰來幹”的問題——都沒人幹,於是只能讓框架幹了。從此以後,

對於Model而言:“老子就管寫,你愛讀不讀。反正我的值是對的,使用者看到展現不對那都賴你。”

對於UI而言:“老子就歇著,你愛咋樣就來弄我兩下,但是活兒得好,別讓我太累,使用者嫌卡那就怪你。”

至於Model如何Drive UI,Angular(髒檢查)、React(Virtual-DOM)用的辦法是主動的發現Model的變化,然後去推動UI更新;Avalon、Vue基於property getter的做法是被動的等Model發生變化。
除了Virtual-DOM以外,都需要對UI進行預處理,解析出一個UI Element -> property之間的依賴關係,知道每一個Element依賴了Model的哪個欄位。把這張圖反過來,就知道當一個property被修改時,它會影響那些個Element,從而實現最小更新。
而Virtual-DOM的最小化patch方案是通過tree-diff計算出來的,基於現代瀏覽器“老子for迴圈跑的飛快”的霸氣,執行tree-diff的速度很理想。於是就直接不需要構建依賴關係,用起來更簡單粗暴;進而在需要的時候有一定的優化空間,可以通過immutable這種方式來快速跳過tree-diff當中的某些環節。
所以在精心優化的情況下,Virtual-DOM應該最快的無疑,property getter有更強的適應性,天生就很快,但從外部去優化它很難。
React另一個優勢是它的啟動速度,由於不需要構建依賴關係,甚至是連parse模板都不需要(這一步相當於直接在構建JSX的時候已經做好了),它啟動步驟就短多了,誇張地說,直接render就出來了。
使用property getter的方案對於Model層有非常微弱的侵入性(相比Knockout那是低多了),使用髒檢查和Virtual-DOM對Model層都幾乎沒有侵入性。
當然上面所說的效能差異其實都沒有那麼大啦……只是因為我自己寫過virtual-dom玩具,也看了Vue的原始碼,一點小結而已。

理想和現實的差距

在一個足夠複雜的場景下,如果能踐行Model與UI的依賴關係,程式的可測性(React還是誰來著,也管它叫Predictable,可預測)就有了一定的保障。

但是,很多情況下,沒有那麼理想,比如

  • 很多Model被展現一次就沒事兒了,壓根兒就沒有動態修改
  • 很多Model只被在一處展現,因此它動態修改的時候,在UI改和在Model裡改,工作量是一樣的
  • UI的調整並沒有那麼理想化,無法解釋為純UI的問題,幾乎每次調整都涉及到業務邏輯的調整
  • 無所謂檢視邏輯和業務邏輯,我們認為展現形式是業務邏輯的一部分,並不是什麼卵的檢視邏輯

個人的感受

  • 程式怎麼寫,還得看活兒
  • 做Web App和做Web Page,取捨還是差別大
  • 怎麼算Web App怎麼算Web Page,還得看老闆怎麼想
  • 如若無所謂模式,無所謂架構,那一切都是白說,反正It works
  • 面向工資程式設計,終究還是為了出活兒快、下班早,需求變時別罵娘,早日升職加薪,當上總經理,迎娶白富美,走上人生巔峰

相關文章