14-觀察者模式和發布訂閱的區別/vue響應式是發布訂閱模式和觀察者模式


1.觀察者模式是只有兩個對象:目標對象(類)去直接作用觀察者(類)去更新,這個更新是在觀察者內部調用自身的update方法去執行響應或者說去做更新。

耦合度較高,因為觀察者是在目標對象的“體內”去執行的。目標對象在自己體內去添加觀察者列表,然后調用自身的發布事件觸發觀察者調用自己的update方法執行。

按照上面的思路寫一個簡易版的觀察者模式:

 1   // 觀察者模式:
 2  class Observer {  3  constructor(cb) {  4                     // 判斷傳進來的是不是一個函數,如果是就掛載到this上
 5                     if (Object.prototype.toString.call(cb) !== "[object Function]") {  6                         console.log('不是一個函數');  7                     } else {  8                         this.cb = cb;  9  } 10  } 11                 // 執行更新
12  update() { 13                 this.cb(); 14  } 15  } 16         // 目標對象
17  class Target { 18  constructor() { 19                 // 存儲觀察者
20                 this.observerList = []; 21  }; 22             // 添加觀察者
23  add(observer) { 24                 this.observerList.push(observer) 25  }; 26             // 通知所有觀察者觸發更新
27  notify() { 28                 for (let i = 0; i < this.observerList.length; i++) { 29                     this.observerList[i].update(); 30  } 31  } 32  } 33 
34         let fn1 = function() {console.log('我要觸發了');} 35         let fn2 = function() {console.log('我要觸發了2');} 36         let o1 = new Observer(fn1); 37         let o2 = new Observer(fn2); 38 
39         let t = new Target(); 40         // 把o1和o2兩個觀察者存入觀察者數組
41  t.add(o1); 42  t.add(o2); 43         // 觸發依次調用所有觀察者的update方法,像不像addEventLister?
44         // 比如給一個按鈕綁定點擊事件,對應不同的回調函數,這里的fn1和fn2就相當於addEventListener中的回調函數
45         t.notify();

輸出:

 

vue響應式觀察者模式部分:

 

 調用: Dep.target就是watcher實例,相當於new Watcher的行為,把實例添加進入了目標對象中,目標對象中的notify就執行了。


 2.發布訂閱模式

 簡單版本自寫:

版本1:模仿vue響應式的發布訂閱

 1  // 收集依賴-相當於vue的Observer中definedPrototype中的get
 2  class Observer {  3  constructor() {  4             this.saveArr = [];  5  }  6  add(fn) {  7             this.saveArr.push(fn);  8             return this.saveArr;  9  } 10  } 11     // 調度中心- 相當於Vue的Dep
12  class Dep { 13  constructor() { 14             this.getArr = []; 15  } 16  on(event) { 17             let ob = new Observer(); 18             this.getArr = ob.add(event); 19  }; 20  emit(event) { 21             let notify = new Notify(); 22  notify.update(event); 23  } 24  } 25     // 發布者 - 相當於vue的Observer中definedPrototype中的set
26  class Notify { 27  constructor() { 28             this.arr = []; 29  } 30  update(fn) { 31             this.arr.push(fn); 32             for (var i = 0; i < this.arr.length; i++) { 33                 this.arr[i](); 34  } 35  } 36  } 37 
38     var fn1 = function() { 39         console.log('1'); 40  } 41     var fn2 = function() { 42         console.log('2'); 43  } 44     var d1 = new Dep(); 45     var d2 = new Dep(); 46 
47  d1.on(fn1); 48  d2.on(fn2); 49  d1.emit(fn1); 50  d2.emit(fn2); 51     // 輸入1,2 

版本2:一個class類,類本身是一個調度中心,里面的on作為訂閱者,emit是發布者

 1  var f1 = function() {  2    console.log('f1');  3  }  4  var f2 = function() {  5    console.log('f2');  6  }  7     // EventEmitter這個類本就是一個調度中心
 8  class EventEmitter {  9  constructor() { 10             this.getArr = {}; 11  } 12         // 訂閱
13  on(type, event) { 14             if (type) { 15                 this.getArr[type] = []; 16                 this.getArr[type].push(event); 17  } 18  }; 19         // 發布
20  emit(type, event) { 21             for (var i = 0; i < this.getArr[type].length; i++) { 22                 this.getArr[type][i](); 23  } 24  } 25  } 26 
27     var ee = new EventEmitter(); 28     ee.on("test1", f1); 29     ee.on("test2", f2); 30     ee.emit("test1", f1); 31     ee.emit("test2", f2); 32     // 輸出: f1,f2

3.vue中的數據響應式其實是個發布訂閱模式:

數據劫持中,get函數是觸發監聽,把data都綁定上了watcher

set函數是發布者,因為只要數據已更新,就會觸發set函數,set函數就會告訴Dep讓其執行notify,相當於this.$emit('notify'),set函數扮演的就是甲方,是個發號施令的角色。

Dep是調度中心,負責收集依賴和收到發布者的命令執行notify方法,調度中心通知Wather執行更新方法。watcher去調用自己的update方法,在compile編譯模板的時候,new Watcher的時候,拿到了Watcher的回調函數(得到set修改后最新值)然后去通過textContent通過打補丁的方式,經歷Diff算法,最終更新DOM。

get函數是訂閱者,訂閱wather

簡單來說就是:

調度者作為訂閱者和發布者的紐帶,就像一個房產中介一樣,租戶就好比訂閱者,去關注中介的發的租房信息,房東就好比發布者,去告訴中介他房子要出租了。

中介(Dep)就收集了很多租房者的微信(watcher),如果房東有房子告訴中介你去發微信(這個行為就是發布者知道調度者的方式notify)告訴租戶們(set方法),就會立馬通過微信通知(notify)租房者(watcher),租房者會去跟媳婦商量商量做出響應(update更新:跟虛擬DOM切磋,diff算法,patch打補丁)


 上面說的不咋對,新的理解:

訂閱階段:

在最開始初始化vue階段,走到模板編譯階段,也就是compile,加載compile.js的時候,回去new Watcher()實例,那么就會觸發watcher中執行 把watcher實例給到 數據劫持的get方法(訂閱者),給每一個data都加上了wather,這些watcher又被push進去了調度中心Dep。

 發布階段:當data數據發生改變后,就會觸發set方法(發布者),set方法中就會讓調度中心Dep去讓他里面的watcher執行他們的update方法,watcher中的update方法呢,又會調用Watcher類中傳入的回調函數,這個回調函數執行,並且傳入set改了那個數據的最新值,這樣complie.js中 去new Watcher實例的時候(數據每次發生變化的時候,就會觸發beforeUpdate和upDate鈎子進行重新編譯,自然就會觸發complie.js),傳入的回調函數就可以收到Wather實例中執行的回調函數的值。拿到值后,去更新DOM。

截幾個關鍵點:

complie.js中:頁面初始化,new Watcher,觸發Wather中的方法(觸發數據劫持的get方法,添加監聽器)

等到data數據發生變化,要重新編譯模板,又會觸發compile.js,自然又觸發了Wather,拿到watcher的回調參數,更新DOM

 數據劫持:

 

 Dep:

 Wather:


 

小小參考:

https://www.cnblogs.com/wenbinjiang/p/12054308.html

https://juejin.cn/post/6844904102585974792


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM