原稿:语雀 · 【原理一】 Vue2.x 响应式原理 · 原目录:Vue › 【原理一】 Vue2.x 响应式原理

原理描述

【原理一】 Vue2.x 响应式原理 配图

Vue 的响应式是通过 Object.defineProperty 对数据进行劫持,并结合观察者模式实现:

  • Vue 为每个对象创建一个 observer,之后利用 Object.defineProperty 劫持监听对象所有属性,把这些属性全部转为 getter 和 setter
  • Vue 中每个组件实例都会对应一个 watcher 实例:在组件渲染的过程中,watcher 会通过 getter 订阅到当前属性上,即该属性成为组件的一项依赖,首次渲染后,组件会跟踪一组依赖列表。之后当属性的 setter 触发时,observer 会通知订阅该属性的 watcher,从而使它关联的组件重新渲染

源码分析

Vue2.6 版本初始化响应数据的过程大致如下

【原理一】 Vue2.x 响应式原理思维导图
- new Vue():调用构造函数,当中会调用 initMixin(),依次执行一系列 init 方法 - initState():开始初始化 watchers\[\] 数组,调用 initProps()、initMethods()、initData()
image.png

initState()

  • initData():对 data 进行初步处理后传入 observe() 进行观测
image.png

initDate()

  • observe():为传入 value 创建 observer ,成功观测则返回观测者实例,或返回当前 value 已有的 observer
image.png

observe()

  • Observer() 构造器:observer 会被附加到被观测对象上,之后 observer 会将对象的 property keys 转换为 getter/setters , getter 会在渲染过程中收集当前 property 的依赖项,setter 则会分发更新
  • 每一个 observer 实例也拥有自己的 dep 实例,负责依赖收集
  • def() 将 observer 实例添加为被观测数据对象上的属性 __ob__
  • 创建 observer 后,执行 walk()
image.png

Observer()

  • walk():遍历 properties,对每一个property 执行 defineReactive()
image.png

Observer.prototype.walk()

  • defineReactive():真正进行转换的地方,首先会调用 observe,递归地对当前 property 中的子对象添加观测,即重复 Observer() => walk() => defineReactive()。之后通过 defineProperty() 添加 getter 和 setter
  • target:根据注释,target 即当前执行时的 watcher
image.png

Dep.target

  • getter:在 getter 中可以看到调用了 depend() ,查看 depend() 声明,该函数调用 target.addDep(),即渲染实例时调用 getter() 就会添加 dep 实例到 watcher
  • setter:在 setter 中调用 notify(),修改值的同时该更新会通知 watcher,其中关联的组件会进行更新
image.png

defineReactive()

image.png
image.png

Dep.prototype.depend()/notify() Watcher.prototype.addDep()

过程几个主要的参与者

  • watcher:每一个 vue 组件会对应一个 watcher,赋值解析表达式、收集依赖关系,并在表达式值发生变化时启动回调(响应式更新)
  • observer:vue 实例的 data 和 其中的对象都会被附加一个 observer,该 observer 会劫持对象固有属性,转换为 getter/setters 
  • dep:每一个响应式属性会对应一个 dep,Dep 类实际上是发布订阅模式的实现,subs[] 中保存了订阅在当前属性变化上的 watcher,dep.target 是当前的 watcher

进行源码分析后可以解释开始图片中两个重要过程的实现细节:

  • 收集依赖:渲染实例时必须通过 getter 获取属性,则当前组件实例的 watcher 会被存入 observer 的 dep 中,即该实例依赖于该属性
  • 通知:通过 setters 修改对象 property 时,对应属性的 dep 就会通知自己保存的所有 watcher,从而重新渲染