MVVM实现原理

Vue 双向绑定采用的是 MVVM 模式。监听器 Observer 、订阅器 Dep、订阅者 Watcher、解析器 Compile。

  • Compile 解析器:扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。
  • Observer 监听器:调用 defineReactive 劫持并监听所有属性,getter 向 Dep 依赖。
  • Dep 订阅器:收集观察者 Watcher 和通知观察者目标更新。每个属性拥有自己的消息订阅器dep,用于存放所有订阅了该属性的观察者对象,当数据发生改变时,通知所有的 watch 执行自己的update逻辑。
  • Watcher 订阅者:观察属性提供回调函数以及收集依赖(如计算属性computed,vue会把该属性所依赖数据的dep添加到自身的deps中),当被观察的值发生变化时,会接收到来自dep的通知,从而触发回调函数。
  • Watcher类的实现比较复杂,因为他的实例分为渲染 watcher(render-watcher)、计算属性 watcher(computed-watcher)、侦听器 watcher(normal-watcher)三种。
    • computed-watcher:我们在组件钩子函数computed中定义,这类 watcher 有个特点:当计算属性依赖于其他数据时,属性并不会立即重新计算,只有之后其他地方需要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)特性。
    • normal-watcher:我们在组件钩子函数watch 中定义,即只要监听的属性改变了,都会触发定义好的回调函数。
    • render-watcher:每一个组件都会有一个 render-watcher,当 data/computed 中的属性改变的时候,会调用该 render-watcher 来更新组件的视图。
    • 这三种 watcher 也有固定的执行顺序,分别是:computed-render -> normal-watcher -> render-watcher。尽可能的保证,在更新组件视图的时候,computed 属性已经是最新值了,如果 render-watcher 排在 computed-render 前面,就会导致页面更新的时候 computed 值为旧数据。
  • 而 Dep 订阅器和 Watcher 订阅者又是一种观察者模式。Watcher 用来订阅属性的变化通,从而更新视图。Dep 用来收集 Watcher 的依赖,当 Observer 更新时,通过 dep.notify() 统一派发给 Watcher,实现了双向绑定。
  • 综上:简单来说通过数据劫持+发布订阅模式,通过以下初始化和更新的过程来实现双向绑定,也就是响应式原理。
  • 初始化:
  • 1.Observer 对数据进行响应式绑定
  • 2.Compiler 编译解析模块指令,初始化渲染页面,并将每个指令的节点绑上更新函数,实例化监听监听数据的订阅者 Watcher。
  • 3.数据 getter 时,执行对应数据的 dep 收集所有 watcher 依赖
  • 更新:
  • 1.更新时触发 dep.notify(),派发通知所有订阅者 watcher
  • 2.订阅者 watcher 执行 update() 回调函数
  • 3.调用对应 Compiler 编译解析模块,重新更新视图
  • Vue源码相关 - 图1