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

Vue 的响应式是通过 Object.defineProperty 对数据进行劫持,并结合观察者模式实现:
- Vue 为每个对象创建一个 observer,之后利用 Object.defineProperty 劫持监听对象所有属性,把这些属性全部转为 getter 和 setter
- Vue 中每个组件实例都会对应一个 watcher 实例:在组件渲染的过程中,watcher 会通过 getter 订阅到当前属性上,即该属性成为组件的一项依赖,首次渲染后,组件会跟踪一组依赖列表。之后当属性的 setter 触发时,observer 会通知订阅该属性的 watcher,从而使它关联的组件重新渲染
源码分析
Vue2.6 版本初始化响应数据的过程大致如下

initState()
- initData():对 data 进行初步处理后传入 observe() 进行观测

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

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

Observer()
- walk():遍历 properties,对每一个property 执行 defineReactive()

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

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

defineReactive()


Dep.prototype.depend()/notify() Watcher.prototype.addDep()
过程几个主要的参与者
|
进行源码分析后可以解释开始图片中两个重要过程的实现细节:
- 收集依赖:渲染实例时必须通过 getter 获取属性,则当前组件实例的 watcher 会被存入 observer 的 dep 中,即该实例依赖于该属性
- 通知:通过 setters 修改对象 property 时,对应属性的 dep 就会通知自己保存的所有 watcher,从而重新渲染