响应式系统是其核心特性之一,主要涉及到Watcher、Dep(通常表示依赖)等概念
一、响应式原理概述
Vue 2 使用 Object.defineProperty () 方法将对象的属性转换为 getter 和 setter,从而实现对数据的响应式追踪。当数据被读取时,会收集依赖(通常是Watcher实例);当数据被修改时,会通知依赖进行更新。
二、Watcher
-
作用:
Watcher是一个观察者,它观察特定的响应式数据,并在数据变化时执行相应的回调函数。通常在计算属性、侦听器以及组件的渲染过程中创建Watcher实例。- 例如,在组件的渲染过程中,每个组件实例都会创建一个渲染
Watcher,它观察组件的所有响应式数据,当这些数据变化时,触发组件的重新渲染。
-
工作过程:
-
在创建
Watcher实例时,它会立即执行其get方法,这个方法会触发响应式数据的 getter,从而收集当前的依赖。 -
当
Watcher观察的数据发生变化时,会触发它的回调函数,执行相应的更新操作。
-
-
创建时机:
- vue实例内有
watch的时候,initState->initWatch->createWatcher - vue实例内有
computed的时候,initState->initComputed - 逻辑内有
vm.$watch的时候,会调用stateMixin->Vue.prototype.$watch - 在挂载调用
$mounted的时候mountComponent
- vue实例内有
三、Dep(依赖收集器)
-
作用:
Dep用于收集依赖,它维护了一个Watcher列表。当响应式数据被读取时,会将当前正在读取数据的Watcher添加到Dep中。- 当数据变化时,
Dep会通知所有收集到的Watcher进行更新。
-
工作过程:
- 在响应式数据的 getter 中,会创建一个
Dep实例(如果不存在的话),并将当前正在读取数据的Watcher添加到Dep的subs列表中。 - 在响应式数据的 setter 中,会遍历
Dep中的Watcher列表,通知每个Watcher进行更新。
- 在响应式数据的 getter 中,会创建一个
-
创建时机:
- 把数据转化为响应式的调用栈
initState->initData/observe->new Observer->walk/observeArray->defineReactive - 在
new Observer的内部- 引用数据做响应式,会通过
this.dep = new Dep()创建dep的实例,作为Observer实例的实例属性 - 简单类型数据做响应式,会通过
defineReactive函数内的const dep = new Dep()弄一个闭包来保存dep - 会通过
def(value, '__ob__', this);把这个Observer实例挂载到这个引用数据上面 - 通过
defineReactive会设置getter,内部会有dep.depend()逻辑,用于访问数据的时候,收集相关dep - 通过
defineReactive会设置setter,内部会有dep.notify()逻辑,用于设置数据的时候,通知所有dep执行所有subs的update更新视图
- 引用数据做响应式,会通过
- 把数据转化为响应式的调用栈
四、通知机制
-
数据变化触发通知:
- 当响应式数据被修改时,会触发其 setter。在 setter 中,会通知相关的
Dep实例,Dep再通知所有收集到的Watcher进行更新。 - 例如,当一个响应式数据属性被赋值为新的值时,会触发其 setter,进而通知所有依赖这个属性的
Watcher。
- 当响应式数据被修改时,会触发其 setter。在 setter 中,会通知相关的
-
Watcher的更新过程:- 当
Watcher收到通知后,会执行其回调函数。对于渲染Watcher来说,回调函数通常会触发组件的重新渲染。对于计算属性的Watcher来说,回调函数会重新计算计算属性的值。 - 在更新过程中,可能会再次触发响应式数据的读取,从而收集新的依赖,形成一个更新循环。
- 当
-
如果没有对应的
watcher,则不会收集对应的dep,因为用于保存收集响应式数据的dep的this.deps挂载在Wather的实例上(注意,对应的dep是在响应式数据上的,不要与deps弄混) -
如果没有不是响应式数据,自然也不会生成对应的
Dep实例,也不会收集对应的wather,因为根本不存在相应的响应式数据的dep(用于收集wather的subs数组,是dep的实例属性) -
dep的subs和watcer的deps是相互收集,对多对的关系
Vue 2 的响应式系统通过Watcher、Dep等机制实现了数据变化的自动检测和视图的自动更新,使得开发者可以专注于业务逻辑,而不必手动管理数据和视图的同步。