Vue的观察者模式和发布/订阅模式

192 阅读4分钟

观察者模式和发布/订阅模式

观察者模式

观察者模式是使用一个subject目标对象维持一系列依赖于它的observer观察者对象,将有关状态的任何变更自动通知给这一系列观察者对象.当subject目标对象需要告诉观察者发生了什么事情时,它会向观察者对象们广播一个通知.

如上图所示:一个或多个观察者对目标对象的状态感兴趣时,可以将自己依附在目标对象上以便注册感兴趣的目标对象的状态变化,目标对象的状态发生改变就会发送一个通知消息,调用每个观察者的更新方法.如果观察者对目标对象的状态不感兴趣,也可以将自己从中分离.

发布/订阅模式

发布/订阅模式使用一个事件通道,这个通道介于订阅者和发布者之间,该设计模式允许代码定义应用程序的特定事件,这些事件可以船费自定义参数,自定义参数包含订阅者需要的信息,采用事件通道可以避免发布者和订阅者之间产生依赖关系.

微信截图_20210923151814.png

两者的区别

观察者模式,允许观察者实例对象(订阅者)执行适当的事件处理程序来注册和接收目标实例对象(发布者)发出的通知(即在观察者实例对象上注册update方法),使订阅者和发布者之间产生了依赖关系,且没有事件通道.不存在封装约束的单一对象,目标对象和观察者对象必须合作才能维持约束.观察者对象向订阅它们的对象发布感兴趣的事件.通信只能是单向的. 发布/订阅模式: 单一目标通常有很多观察者,有时一个目标的观察者是另一个观察者的目标,通信可以实现双向.该模式存在不稳定性,发布者无法感知订阅者的状态.

Vue的运行机制简述

微信截图_20210923153050.png

初始化流程

  • 创建Vue实例对象

  • init过程会初始化生命周期,初始化事件中心,初始化渲染,执行beforeCreate,周期函数,初始化data,props,computed,watcher,执行created周期函数等.

  • 初始化后,调用$mount方法对Vue实例进行挂载(挂载的核心过程包括模板编译,渲染以及更新三个过程)

  • 如果没有在Vue实例上定义render方法而是定义了template,那么需要经历编译阶段.需要先将template字符串百编译成render function,template字符串编译步骤如下:

  • parse正则解析template字符串形成AST(抽象语法树,是源代码的抽象语法结构的树状表现形式)

  • optimize标记静态节点跳过diff算法(diff算法是逐层进行比对,只有同层级的节点进行比对,因此时间的复杂度只有O(n).

  • generate将AST转化成render function字符串

  • 编译成render function后,调用$mount的mountComponent方法,先执行beforeMount钩子函数,然后核心是实例化一个渲染Watcher,在它的回调函数(初始化的时候执行,以及组件实例中检测到数据发送变化时执行)中调用updateComponent方法(此方法调用render方法生成虚拟Node,最终调用update方法更新DOM)

  • 调用render方法将render function渲染成虚拟的Node(真正的DOM元素是非常庞大的,因为浏览器的标准就把DOM设计的非常复杂,如果频繁的去做DOm更新,会产生一定的性能问题,而VirtualDOM就是用一个原生的JavaScript对象去描述一个DOM节点,所以它比创建一个DOM的代价要小很多,而且修改属性也很轻松,还可以做到跨平台兼容), render方法的第一个参数是createElement(或者说是h函数),这个在官方文档也有说明.

  • 生成虚拟DOM树后,需要将虚拟DOM树转化成真是的DOM节点,此时需要调用update方法,update方法又会调用pacth方法把虚拟DOM转换成真正的DOM节点.需要注意在图中忽略了新建真是DOM的情况(如果没有旧的虚拟Node,那么可以直接通过createElm)创建真实DOM节点.这里重点分析在已有虚拟Node的情况下,会通过sameVNode判断当前需要更新的Node节点是否和旧的Node节点相同(例如我们设置的key属性发生了变化,那么节点显然不同),如果节点不同那么将旧节点采用新节点替换即可,如果相同且存在子节点,需要调用patchVNode方法执行diff算法更新DOM,从而提升DOM操作的性能.