zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    phpstorm 中文版 支持BUG调试 IDE
    WINDOWS中设置计划任务执行PHP文件
    数据库 Navicat_Premium_11.0.10 破解版下载安装
    zend Studio10.6.2破解注册码
    zend Studio10.6.2 下载
    【jdbcTemplate】使用jdbcTemplate查询的三种回调
    【JDBC】向数据表插入数据时,自动获取生成的主键
    【转】JDBC为什么要使用PreparedStatement而不是Statement
    【Spring学习笔记-6】关于@Autowired与@Scope(BeanDefination.SCOPE_PROTOTYPE)
    【java基础学习-2--】关于Hashcode()的使用
  • 原文地址:https://www.cnblogs.com/haoqiyouyu/p/14665288.html
Copyright © 2011-2022 走看看