前言

在面试题中经常会出现与“发布订阅”模式相关的题目,比如考察我们对Vue响应式的理解,也会有题目直接考验我们对“发布订阅”模式或者观察者模式的理解,甚至还会有一些手写算法题。笔者就在今年三月参加某安全公司的面试时被要求手写代码实现“发布订阅”模式,当时由于没有准备没有回答上来,悔不该当初。由此可见“发布订阅”模式是一个非常重要的设计模式,接下来我们一起学习下吧。

观察者模式 vs “发布订阅”模式

首先需要澄清的事,这两者虽然相似,却有不同。

观察者模式

只涉及两个关键角色,发布者与订阅者。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。

发布者的行为:

  1. 增加订阅者
  2. 移除订阅者
  3. 通知所有订阅者

代码实现如下:

 // 定义发布者类class Publisher {constructor() {// 创建订阅者this.observers = []}// 增加订阅者add(observer) {this.observers.push(observer)}// 移除订阅者remove(observer) {this.observers.map((item, i) => {if (item === observer) {this.observers.splice(i, 1)}})}// 通知所有订阅者notify() {this.observers.map((observer) => {observer.update(this)})}
}

订阅者行为:

  1. 被通知
  2. 去执行

代码实现:

 // 定义订阅者类  class Observer {constructor() {console.log('创建订阅者')}update() {console.log('订阅者更新')}
}

“发布订阅”模式

与观察者模式类似,但是在这种模式下发布者完全不用感知订阅者,不用关心它怎么实现回调方法,事件的注册和触发都发生在独立于双方的第三方平台(事件总线)上。发布-订阅模式下,实现了完全地解耦。

vue2.x响应式原理探究

响应式实现过程官方解释

  1. 在Vue data中存入对象数据
  2. Vue遍历此对象每个property,通过Object.defineProperty()方法进行数据劫持,给每一个property加上getter/setter
  3. 每个组件实例都对应一个Watcher实例,会收集所有接触过的property依赖,如果数据有变化,将会收到通知,watcher会使其关联的组件实例改变

深入理解响应式原理 官网文档v2

发布订阅模式在vue响应式原理中的具体运用

初始化过程

class Vue {  constructor(options) {  this.$options = options;  this.$data = options.data;  // 对data选项做响应式处理  observe(this.$data);  // 代理data到vm上  proxy(this);  // 执行编译  new Compile(options.el, this);  }
}

发布者/伪订阅者:Observe

observe在前文中代表了订阅者,但这里他更多的是一个发布者,执行了发布者的权利,增加了订阅者,并且在改变时通知了订阅者。

function observe(obj) {  if (typeof obj !== "object" || obj == null) {  return;  }  new Observer(obj);
}  class Observer {  constructor(value) {  this.value = value;Object.keys(value).forEach((key) => {  defineReactive(value, key, value[key]);  }); }
}

defineReactive是一个非常重要的方法,为每⼀个key创建⼀个Dep实例

function defineReactive(obj, key, val) {  // 递归遍历 确保深层次key也能够响应this.observe(val);  // 对每个key都建立一个Dep管家const dep = new Dep();  // 使用defineProperty对每个key建立getter/setterObject.defineProperty(obj, key, {  get() {  Dep.target && dep.addDep(Dep.target);// Dep.target也就是Watcher实例  return val;  },  // 监听变化set(newVal) {  if (newVal === val) return;  // 通知dep执行更新方法 dep.notify(); },  });
}

事件中心:Dep管家,管理真实订阅者Wacther

Dep收集了组件实例中同一个key对应的所有订阅者Wacther

// 发布者
class Dep {  constructor() {  this.deps = [];  // 依赖管理  }  addDep(dep) {  this.deps.push(dep);  }  notify() {   // 实际上是调用的watcher中的更新事件this.deps.forEach((dep) => dep.update());  }
}

实际的订阅者:Watcher

通过触发get,将watcher添加到key对应的Dep中

// 订阅者 负责更新视图
class Watcher {  constructor(vm, key, updater) {  this.vm = vm  this.key = key  this.updaterFn = updater  // 创建实例时,把当前实例指定到Dep.target静态属性上  Dep.target = this  // 读一下key,触发get 便将watcher添加到key对应的Dep中  vm[key]  // 置空  Dep.target = null  }  // 未来执行dom更新函数,由dep调用的  update() {  this.updaterFn.call(this.vm, this.vm[this.key])  }
}

VUE2.X响应式的局限性

由于js的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。

对象

Vue 无法检测 property 的添加或移除,因为Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以只有在初始化实例时就存在于data的property才是响应式的。

对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value) 方法或者其别名vm.$set向嵌套对象添加响应式 property。例如:

Vue.set(vm.someObject, 'b', 2)this.$set(this.someObject,'b',2)

为已有对象赋值多个新 property,需用原对象与要混合进去的对象的 property 一起创建一个新的对象。

// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

数组

Vue 不能检测以下数组的变动:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue

  2. 当你修改数组的长度时,例如:vm.items.length = newLength

为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将在响应式系统内触发状态更新:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:

vm.$set(vm.items, indexOfItem, newValue)

为了解决第二类问题,你可以使用 splice

vm.items.splice(newLength)

感谢

本篇文章讨论了发布订阅模式以及观察者模式,同时也讨论了发布订阅模式在vue2.x响应式中的应用,如有不对,欢迎指正,下一篇文章我将继续探究vue3.x的响应式原理,如果觉得写的还行就帮我点个赞吧,这样我会更有动力进行接下来的知识输出!谢谢各位朋友的观看!

Vue响应式原理探究之“发布-订阅”模式相关推荐

  1. 【2019 前端进阶之路】深入 Vue 响应式原理,活捉一个 MVVM

    作者:江三疯,专注前端开发.欢迎关注公众号前端发动机,第一时间获得作者文章推送,还有各类前端优质文章,致力于成为推动前端成长的引擎. 前言 作为 Vue 面试中的必考题之一,Vue 的响应式原理,想必 ...

  2. vue 数组删除 dome没更新_详解Vue响应式原理

    摘要: 搞懂Vue响应式原理! 作者:浪里行舟 原文:深入浅出Vue响应式原理 Fundebug经授权转载,版权归原作者所有. 前言 Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是 ...

  3. Vue响应式原理 vue源码(十一)

    前言 看过很多讲响应式的文章,大多都是告诉你们,有Observer,Dep,Wathcer类,Object.definePorperty,先会触发get中的dep.depend收集依赖,然后数据改变时 ...

  4. Vue响应式原理的简单模型

    1.前言 最近在梳理vue响应式的原理,有一些心得,值得写一篇博客出来看看. 其实之前也尝试过了解vue响应式的原理,毕竟现在面试看你用的是vue的话,基本上都会问你几句vue响应式的原理.以往学习这 ...

  5. Vue响应式原理(看这一篇就够了)

    你肯定听说过Object.denfineProperty或是Proxy\reflect,这的确是在VUE响应式原理中起重要作用的一部分代码,但这远远不能代表整个流程的精妙.上图: 不懂没关系,请往下看 ...

  6. Vue源码--解读vue响应式原理

    原文链接:https://geniuspeng.github.io/2018/01/05/vue-reactivity/ Vue的官方说明里有深入响应式原理这一节.在此官方也提到过: 当你把一个普通的 ...

  7. 一篇文章带你吃透VUE响应式原理

    本篇响应式原理介绍比较长,全文大概1w+字.虽然内容繁杂,但阅读过后,绝对会让你对vue的响应式有更加深刻的理解. 分块阅读,效果更佳.(建议读者有一定vue使用经验和基础再食用) 首先上图,下面这张 ...

  8. 模拟Vue响应式原理

    模拟Vue响应式原理 设计模式 发布订阅模式 我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信 号,其他任务可以向 ...

  9. Vue响应式原理简述

    Vue响应式原理简述 依赖技术 图解过程 依赖技术 问题:Vue如何监听data的改变? => 技术:Object.defineProperty函数.在其中的set方法中监听对象属性的改变. 问 ...

最新文章

  1. 小猿圈之git 的几个好用自定义命令
  2. Timer的缺陷 用ScheduledExecutorService替代
  3. 一文了解web无状态会话token技术JWT
  4. 基于React开发范式的思考:写在Lesx发布之际
  5. 执行shell出现bad interpreter
  6. javaweb实训第五天下午——xml配置文件约束报错问题
  7. iosTableView 局部全部刷新以及删除编辑操作
  8. Mac 用快捷键选取文字,省时省力提高工作效率
  9. Caffe学习:pycaffe利用caffemodel进行分类=批处理
  10. mergsort.c
  11. 推桌子nyoj 220hdu1050 (贪心算法系列)
  12. mysql表analyze_MySQL ANALYZE Optimize Check Table使用详解
  13. 复旦大学邱锡鹏老师深度学习课程笔记(一)—— 一些模型概述
  14. Win10不能禁和不建议禁的服务以及禁用后的影响
  15. hdu2201:熊猫阿波的故事
  16. 高清格式——720p/1080i/1080p,谁更清晰
  17. Android真机连接局域网PC服务器的方法
  18. 手把手教你:图像识别的垃圾分类系统
  19. python中模块打补丁
  20. iOS7设置状态栏颜色

热门文章

  1. docker 监控报警以及日志分析
  2. steam饥荒服务器正在运行旧版本模组,Don’t Starve Together(饥荒)服务器搭建
  3. 习题2.7 弹球距离 (15分) 浙江大学数据结构
  4. vue 选取某月、当月第一天和最后一天处理
  5. 微信小程序 input、textarea设置宽度100%,超出父元素的宽度
  6. MySQL 建表时的四种key
  7. shopex木马清除 DNS劫持清除
  8. Docker build失败
  9. 放大的概念和放大电路的主要性能指标
  10. Cadence PCB仿真使用Allegro PCB SI通过导入工艺文件配置层叠结构的方法图文教程