1.背景
平時使用過 Vue 這個前端架構的同學,對于資料綁定這個詞肯定不會陌生,進一步,它與 react 有點不同的是它還有一個雙向資料綁定 v-model。
資料綁定的方式能夠極大程度上友善我們開發,不用去進行繁瑣的 DOM 操作,這也是 MVVM 架構的一個極大的優勢所在。
網上閱讀到其他同學對 Vue 源碼解讀的文章,發現大家對 Vue 的依賴收集機制解讀會有些不一樣的方式。有些同學解釋說依賴收集使用的觀察者模式,有些同學則解釋說是釋出/訂閱模式。
因為以前認為這兩個名字隻是不同的叫法而已也就沒太在意,但偶然發現有人問這兩個模式的差別,這個問題突然也引起了我的好奇心,是以也就花了些時間來比較這兩個名詞背後到底有什麼不同。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SMjVjYwUTYyMTYlBTO0gTNlhjZ1AjMmljYwEzNiJzMm9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
2.兩個模式的異同
觀察者模式與釋出-訂閱模式
首先直接上圖。從圖中可以看出,無論是觀察者模式還是釋出-訂閱模式,它們都是一些狀态依賴于某些資料,當資料發生變化,這些狀态也需要進行相應的更新的模式。不同點在于,釋出-訂閱模式比觀察者模式多了個事件中心。
更細緻點說,在觀察者模式中,被觀察者能夠完全感覺到觀察者的存在,一旦發生變化,被觀察者負責将變化通知到所有的觀察者。
舉個例子,假設你要出租一套房子,你在網上釋出了資訊,有很多人聯系你。當你的房子租出去之後,你要将這個資訊通知到剩下的其他人,讓他們去租别的房子。
而在釋出-訂閱模式中,因為有個事件中心的存在,當釋出者釋出新的資訊之後,事件中心會去通知更新。
還是出租房子的例子,你把你要出租房子的資訊告訴中介,同時中介那邊有很多客戶讓中介幫忙找房,當你的房子租出去後,你隻需要告訴中介房源沒了,中介則會去通知意向客戶房源缺失的資訊。
在該模式中,訂閱者向事件中心訂閱某些事件,釋出者并不知道有沒有訂閱者,隻要發生變化,釋出者就會通知事件中心。
下面就用實際的代碼來還原一下這兩個模式。
3.觀察者模式
ObserverList.js
觀察者清單類,由被觀察者管理,添加或移除對應的觀察者。
Observer.js
觀察者類,接到更新通知後,做出一些回報。
Observed.js
被觀察者類,負責維護觀察者,并且将更新通知到所有觀察者。
index.js
4.釋出-訂閱模式
EventCenter.js
事件中心類,包括處理訂閱者的訂閱/退訂事件,以及釋出者釋出事件後的變更通知。
Publisher.js
釋出者類,負責向事件中心釋出變更。
Subscriber.js
訂閱者類,對變更通知做出回報。
index.js
5.小結
總的來說,兩種設計模式都是為了解耦,其中釋出-訂閱模式解耦度更高。也可以認為釋出-訂閱模式是觀察者模式的進一步解耦,這也是有時候會認為這兩個模式一樣。
現在反過來看 Vue 中的依賴搜集,它更多的傾向于觀察者模式,因為對于某個資料的觀察,擁有該資料的對象都能清楚的感覺,當資料變化後,它都要通知到各個依賴對象。
有人認為觀察者模式更多的是同步操作,而釋出-訂閱模式更适合異步操作(引入消息隊列),但就 Vue 來說,它在派發更新過程中,也引入了隊列的概念,Vue 并不是每當資料更新就立馬響應,而是放入一個隊列,在下一個 Tick 中再将該隊列整個重新整理。
6.用釋出-訂閱模式實作資料綁定
既然前面已經知道 Vue 是通過觀察者模式來實作依賴收集的,那這裡就用釋出-訂閱模式來簡單實作一下資料綁定的功能!
說幹就幹!
先讓我們來分析一下做這個例子的流程。
1、首先,我們需要一個事件中心eventCenter,能夠滿足 DOM 節點對響應式資料變化的訂閱,能夠在資料變化後釋出資訊給 DOM 節點。
2、其次,我們需要對所有的響應式資料進行攔截,用Object.defineProperty方法進行改寫,主要是在set函數中通過事件中心釋出變化。
最後,我們需要對管理的 DOM 節點進行周遊,将{{ 響應式變量 }}替換為實際資料,并且對資料進行變更訂閱。
既然流程搞清楚了,那就開始撸代碼吧!
Coding…
- eventCenter.js
- dataBinding.js
- index.html
eventCenter.js就是事件進行中心,dataBinding.js就是完成資料綁定的主要邏輯操作了。
在周遊所有 DOM 節點的時候,對于文本節點,用正規表達式判斷是否引用響應式資料,如果有則進行相應的替換,對于标簽節點,首先判斷是不是input節點,因為需要對v-model做雙向綁定操作,然後再通過遞歸進行子節點的篩選。
結果示範
相較于 Vue 的實際資料綁定的實作,這個 demo 肯定是極其簡略甚至粗糙的(╭(╯^╰)╮)~比如說,一個标簽節點中既有普通文本,又有響應式資料,或者一個标簽裡有多個資料綁定,這些都沒處理。
但通過這個簡單的例子,加深了自己對于 Vue 資料綁定的了解還是很有意義的~