观察者模式

目标者对象和观察者对象有相互依赖的关系,观察者对某个对象的状态进行观察,如果对象的状态发生改变,就会通知所有依赖这个对象的观察者,

目标者对象 Subject,拥有方法:添加 / 删除 / 通知 Observer;

观察者对象 Observer,拥有方法:接收 Subject 状态变更通知并处理;

目标对象 Subject 状态变更时,通知所有 Observer。

Vue中响应式数据变化是观察者模式 每个响应式属性都有dep,dep存放了依赖这个属性的watcher,watcher是观测数据变化的函数,如果数据发生变化,dep就会通知所有的观察者watcher去调用更新方法。因此, 观察者需要被目标对象收集,目的是通知依赖它的所有观察者。那为什么watcher也要存放dep呢?是因为当前正在执行的watcher需要知道此时是哪个dep通知了自己。

在beforeCreate之后,created之前调用observe(data)初始化响应式数据,以下是简化版代码(没有处理数组的劫持)
  1. class Observer {
  2. // 需要对value的属性描述重新定义
  3. constructor(value) {
  4. this.walk(value); // 初始化的时候就对数据进行监控
  5. }
  6. walk(data) {
  7. Object.keys(data).forEach((key) => {
  8. defineReactive(data, key, data[key]);
  9. });
  10. }
  11. }
  12.  
  13. function defineReactive(data, key, value) {
  14. // value 可能是一个对象,要递归劫持,所以数据不能嵌套太深
  15. observe(value);
  16. let dep = new Dep();
  17. Object.defineProperty(data, key, {
  18. get() {
  19. // 如果有 watcher,就让 watcher 记住 dep,防止产生重复的 dep, 同时 dep 也收集此 watcher
  20. if (Dep.target) {
  21. dep.depend();
  22. }
  23. return value;
  24. },
  25. set(newVal) {
  26. // 数据没变动则不处理
  27. if (value === newVal) return;
  28. observe(newVal); // 如果新值是个对象,递归拦截
  29. value = newVal; // 设置新的值
  30. dep.notify(); // 通知收集的 watcher 去更新
  31. },
  32. });
  33. }
  34. function observe(data) {
  35. // 不是对象则不处理,isObject是用来判断是否为对象的函数
  36. if (Object.prototype.toString.call(data)!== '[object Object]') return;
  37. // 通过类来实现数据的观测,方便扩展,生成实例
  38. return new Observer(data);
  39. }
  40. observe(data)
 
在created之后,mouted之前调用mountComponent挂载组件,以下是简化版代码(没有处理watch和computed的watcher)
  1. class Dep {
  2. static target = null
  3. constructor() {
  4. this.id = id++;
  5. this.subs = []; // 存放依赖的watcher
  6. }
  7. depend() {
  8. // 让正在执行的watcher记录dep,同时dep也会记录watcher
  9. Dep.target.addDep(this);
  10. }
  11. addSub(watcher) {
  12. // 添加观察者对象
  13. this.subs.push(watcher);
  14. }
  15. notify() {
  16. // 触发观察者对象的更新方法
  17. this.subs.forEach((watcher) => watcher.update());
  18. }
  19. }
  20. class Watcher {
  21. constructor(vm, exprOrFn) {
  22. this.vm = vm;
  23. this.deps = [];
  24. // 用来去重,防止多次取同一数据时存入多个相同dep
  25. this.depId = new Set();
  26. // exprOrFn是updateComponent
  27. this.getter = exprOrFn;
  28. // 更新页面
  29. this.get();
  30. }
  31. get() {
  32. Dep.target = watcher; // 取值之前,收集 watcher
  33. this.getter.call(this.vm); // 调用updateComponent更新页面
  34. Dep.target = null; // 取值完成后,将 watcher 删除
  35. }
  36. // dep.depend执行时调用
  37. addDep(dep) {
  38. let id = dep.id;
  39. let has = this.depId.has(id);
  40. if (!has) {
  41. this.depId.add(id);
  42. // watcher存放dep
  43. this.deps.push(dep);
  44. // dep存放watcher
  45. dep.addSub(this);
  46. }
  47. }
  48. // 更新页面方法,dep.notify执行时调用
  49. update() {
  50. this.get(); // 一修改数据就渲染更新
  51. }
  52. }
  53. function mountComponent(vm) {
  54. // 渲染更新页面
  55. let updateComponent = () => {
  56. let vnode = vm._render(); // 生成虚拟节点 vnode
  57. vm._update(vnode); // 将vnode转为真实节点
  58. };
  59. // 每个组件都要调用一个渲染 watcher
  60. new Watcher(vm, updateComponent);
  61. }
  62. mountComponent(vm)

发布订阅模式

基于一个事件中心,接收通知的对象是订阅者,需要 先订阅某个事件,触发事件的对象是发布者,发布者通过触发事件,通知各个订阅者。 js中事件绑定,就是发布订阅模式

发布订阅模式相比观察者模式多了个事件中心,订阅者和发布者不是直接关联的。

vue中的事件总线就是使用的发布订阅模式

  1. // 事件总线
  2. class Bus {
  3. constructor() {
  4. // 用来记录事件和监听该事件的数组
  5. this.listeners = {};
  6. }
  7. // 添加指定事件的监听者
  8. $on(eventName, handler) {
  9. this.listeners[eventName].add(handler);
  10. }
  11. // 取消监听事件
  12. $off(eventName, handler) {
  13. this.listeners[eventName].delete(handler);
  14. }
  15. // 触发事件
  16. $emit(eventName, ...args) {
  17. this.listeners[eventName].forEach((fn) => fn(...args));
  18. }
  19. }

事件总线的具体使用可以查看这篇vue之事件总线

观察者模式和发布订阅模式的区别

目标和观察者之间是互相依赖的。

发布订阅模式是由统一的调度中心调用,发布者和订阅者不知道对方的存在。

vue中的观察者模式和发布订阅者模式的更多相关文章

  1. [JS设计模式]:观察者模式(即发布-订阅者模式)(4)

    简介 观察者模式又叫发布---订阅模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 举一个现实生活中的例子,例如小 ...

  2. js中的观察者模式与发布者/订阅者模式的区别?

  3. 浅谈vue响应式原理及发布订阅模式和观察者模式

    一.Vue响应式原理 首先要了解几个概念: 数据响应式:数据模型仅仅是普通的Javascript对象,而我们修改数据时,视图会进行更新,避免了繁琐的DOM操作,提高开发效率. 双向绑定:数据改变,视图 ...

  4. 观察者模式 vs 发布-订阅模式

    我曾经在面试中被问道,_“观察者模式和发布订阅模式的有什么区别?” _我迅速回忆起“Head First设计模式”那本书: 发布 + 订阅 = 观察者模式 “我知道了,我知道了,别想骗我” 我微笑着回 ...

  5. js之观察者模式和发布订阅模式区别

    观察者模式(Observer) 观察者模式指的是一个对象(Subject)维持一系列依赖于它的对象(Observer),当有关状态发生变更时 Subject 对象则通知一系列 Observer 对象进 ...

  6. 观察者模式(Observer)和发布-订阅者模式(Publish/Subscribe)区别

    观察者模式:定义一对多的关系,让多个观察对象同时监听某一个主题对象,主题对象状态发生变化就通知所有观察者对象.所以它是由两类对像组成:Subject主题+Observer观察者.主题发布事件,观察者通 ...

  7. “一切都是消息”--MSF(消息服务框架)之【发布-订阅】模式

    在上一篇,“一切都是消息”--MSF(消息服务框架)之[请求-响应]模式 ,我们演示了MSF实现简单的请求-响应模式的示例,今天来看看如何实现[发布-订阅]模式.简单来说,该模式的工作过程是: 客户端 ...

  8. “一切都是消息”--iMSF(即时消息服务框架)之【发布-订阅】模式

    MSF的名字是 Message Service Framework 的简称,由于目前框架主要功能在于处理即时(immediately)消息,所以iMSF就是 immediately Message S ...

  9. ActiveMQ发布-订阅消息模式

    一.订阅杂志我们很多人都订过杂志,其过程很简单.只要告诉邮局我们所要订的杂志名.投递的地址,付了钱就OK.出版社定期会将出版的杂志交给邮局,邮局会根据订阅的列表,将杂志送达消费者手中.这样我们就可以看 ...

  10. (三)ActiveMQ之发布- 订阅消息模式实现

    一.概念 发布者/订阅者模型支持向一个特定的消息主题发布消息.0或多个订阅者可能对接收来自特定消息主题的消息感兴趣.在这种模型下,发布者和订阅者彼此不知道对方.这种模式好比是匿名公告板.这种模式被概括 ...

随机推荐

  1. mysql实训

    MYSQL You have an error in your SQL syntax; check the manual that corresponds to your MySQL server v ...

  2. 高精度计算_vector

    #include<bits/stdc++.h> using namespace std; // return a+b; vector<int> add(vector<in ...

  3. 钉钉-E应用开发初体验(企业内部应用)

    首先要创建应用,如何创建参考 https://open-doc.dingtalk.com/microapp/bgb96b/gt5d6a 下载 钉钉E应用服务端demo   git clone http ...

  4. laravel qq第三方登录

    QQ互联官网 首先申请成为开发者获取到后面需要的 client_id redirect_uri client_secret 文档资料/SDK及资源下载/SDK下载/PHP SDK 下载下来修改文件修改 ...

  5. C++ 手动实现单向循环链表(课后作业版)

    单向循环链表,并实现增删查改等功能 首先定义节点类,类成员包含当前节点的值, 指向下一个节点的指针 循环链表的尾节点指向头节点 节点定义: //node definition template < ...

  6. eclipse中同步git代码报错checkout conflict with files

    1.Team--->Synchronize Workspace 2.在同步窗口找到冲突文件,把自己本地修改的复制出来 3.在文件上右键选择 Overwrite----->Yes , 4.再 ...

  7. libev中的gcc内嵌函数

    在学习libev的过程中,遇到了大量的gcc内嵌函数,大多是为了提升性能而使用的,这里做一个汇总和介绍,并会持续更新 1.__builtin_expect:该函数是gcc引入的,为的是让程序员讲最有可 ...

  8. jmeter转义

    /reportDetail?md5Key%3De7beff4c73ccd8f1cb01c383eda7fed0%26cid%3D1344500703825739777%26isShow%3Dfalse ...

  9. 添加新模块 import

    import getpass username = input("username") password = input("password") #passwo ...

  10. react+antd upload实现图片宽高、视频宽高尺寸校验

    图片宽高校验方法: // 上传图片尺寸限制 const checkIconWH = (file: any) => { return new Promise<void>(functio ...