單向數據綁定和雙向數據綁定的優缺點,適合什麼場景?
經常看見在vue或者angular的介紹里說自己的特色是雙向數據綁定,而在看react的介紹中,說自己的優勢和特色是單向數據綁定。 這兩個截然不同的機制,為什麼又都能自圓其說呢?在同一個時代里怎麼建立統一的理解?還是說兩種機制有各自適合的最佳場景?
對於非UI控制項來說,不存在雙向,只有單向。只有UI控制項才有雙向的問題。
單向綁定使得數據流也是單向的,對於複雜應用來說這是實施統一的狀態管理(如redux)的前提。
雙向綁定在一些需要實時反應用戶輸入的場合會非常方便(比如多級聯動菜單)。但通常認為複雜應用中這種便利比不上引入狀態管理帶來的優勢。
注意,Vue 雖然通過 v-model 支持雙向綁定,但是如果引入了類似redux的vuex,就無法同時使用 v-model。參見 https://github.com/vuejs/vuex/blob/master/docs/zh-cn/forms.md
先描述一下 ng 跟 vue 中使用雙向綁定的場景。
如賀老 @賀師俊 所言,只有 UI控制項 才存在雙向,非 UI控制項 只有單向。但是就 angular1.x 和 vue1.x 而言,賀老漏掉了一個場景,就是在給自定義組件傳遞數據的時候,也有雙向綁定的情況。在 ng 跟 vue 中分別是這樣的:// angular1.5+
&
angular.module("xx", [])
.component("component", {
bindings: { counter: "=" },
template: "&"
})
// vue1.x
&
Vue.component("component", {
props: ["counter"],
template: "&"
})
當 component 組件里的 button 被點擊時,counter 的變化會同步給 parentCounter 導致父 scope 的數據被改變。(ps:angular1.5 之後組件語法加入了 "&<" 用於單向綁定,vue2 則直接移除了 .sync 語法)
另外一個就是 UI控制項 的場景,對應 ng 里的 ng-model 跟 vue 里的 v-model。
自定義組件的雙向綁定其實是框架在 compile 時識別到相應的語法,然後給相應的 watcher 添加一個 sync flag 及 父級數據索引,好在下次 digest 時同步更新對應父級數據。UI控制項 里的實現就更簡單了,其實就是 one-way binding + auto event binding 的語法糖。比如我們可以用單向綁定實現 ng-model 和 v-model 的同等功能:// ng-model (ps: ng-input 並不是 ng 內置指令,假設它是一個監聽 input 事件的指令)
&
// v-model
&
搞清楚雙向綁定的實現原理之後,可以看到雙綁跟單向綁定之間的差異只在於,雙向綁定把數據變更的操作隱藏在框架內部,調用者並不會直接感知。
單向綁定相應地使得數據流也是單向的,而在踐行單向數據流的 flux 系的實現中,其實不過是在全局搞了一個單例的事件分發器 (dispatcher),開發者必須顯式地通過這個統一的事件機製做數據變更通知。其實這種方式跟框架對 UI控制項 上實現雙向綁定的方式是一樣的。底層都是事件機制。
試想一下,假設在雙向綁定的應用中,我們有辦法 hack 進框架對 UI控制項 自動綁定的事件 listener 或 數據 watcher,然後加上類似 dispatcher 的邏輯,雙向綁定背後的狀態變化我們一樣可以管理起來,一樣可以享用單向數據流才有的收益。對應的,在 react 里同樣可以實現雙向綁定,比如官方的 LinkedStateMixin,只不過它從出生至今就是 deprecated 的。再來看 flux 的這張圖
如果我們做進一步封裝,把 action 跟 dispatcher 都隱藏在框架內部,最後圖就變成這樣了如果再進一步,把相互手動通知的機制再隱藏起來,變成這樣了所以在我看來,雙向和單向只不過是框架封裝程度上的差異,本質上兩者是可以相互轉換的。
回到問題上,單向數據綁定和雙向數據綁定的優缺點,適合什麼場景?
答:
單向綁定的優點是相應的可以帶來單向數據流,這樣做的好處是所有狀態變化都可以被記錄、跟蹤,狀態變化通過手動調用通知,源頭易追溯,沒有「暗箱操作」。同時組件數據只有唯一的入口和出口,使得程序更直觀更容易理解,有利於應用的可維護性。缺點則是代碼量會相應的上升,數據的流轉過程變長,從而出現很多類似的樣板代碼。同時由於對應用狀態獨立管理的嚴格要求(單一的全局store),在處理局部狀態較多的場景時(如用戶輸入交互較多的「富表單型」應用),會顯得啰嗦及繁瑣。基本上雙向綁定的優缺點就是單向綁定的鏡像了。優點是在表單交互較多的場景下,會簡化大量業務無關的代碼。缺點就是由於都是「暗箱操作」,我們無法追蹤局部狀態的變化(雖然大部分情況下我們並不關心),潛在的行為太多也增加了出錯時 debug 的難度。同時由於組件數據變化來源入口變得可能不止一個,新手玩家很容易將數據流轉方向弄得紊亂,如果再缺乏一些「管制」手段,最後就很容易因為一處錯誤操作造成應用雪崩。這樣來看,單向綁定跟雙向綁定在功能上基本上是互補的,所以我們可以在合適的場景下使用合適的手段。比如在 UI控制項 中(通常是類表單操作),我會使用雙向的方式綁定數據;而其他場景則統一採用 單向 + inline event ( &可以說是各有優劣:
單向數據流其實是沒有狀態的, 這使得單向綁定能夠避免狀態管理在複雜度上升時產生的各種問題, 程序的調試會變得相對容易, 但強制要求編碼時設計思維的一致性.
雙向數據流是自動管理狀態的, 但是在實際應用中會有很多不得不手動處理狀態變化的邏輯, 使得程序複雜度上升, 難以調試, 但程序各個部分自成一體, 不強制要求一致的編碼.
如果你的程序需要一個統一的數據源, 應該選擇單向數據流, 所有的數據變化都是可觀測的, 數據自上而下流動, 即使出問題也很容易找到源頭.
如果你的程序本身有多個數據源, 或者是程序的邏輯本身會產生很多的副作用, 應該選擇雙向綁定的程序, 將大項目分化為小項目, 逐個擊破.兩者並不互斥,實際上,在ng2中你可以非常方便的同時使用兩者。全局性數據流使用單向,好跟蹤。局部性數據流使用雙向,簡單。
單向主要的好處是容易追蹤變化,雙向就是寫的爽咯。
雙向綁定只會在做TODOMVC的時候才會感覺代碼少吧,維護起來大點的項目,不能看
推薦閱讀:
※vuejs的.vue文件中的style標籤中的css樣式,背景圖路徑不對?
※JSP渲染比Ajax渲染更安全?(阿里的大神讓我放棄Ajax的想法,說ajax存在安全缺陷)?
※vue 會做出類似 react fiber 的底層改寫嗎?
※Vue.js 的官方文檔是不是太簡略了點?
※vue build 在伺服器build還是在本地build之後放在伺服器?