在 Vue 初始化阶段的时候,会执行 _init
方法,其中在 _init
方法中会执行 initState
方法。在 initState
方法中,我们会对 props
,methods
,data
,computed
,watch
进行初始化。在我们这篇文章中,主要分析的是 Vue 的数据的响应式,所以我们着重分析 props
和 data
。
首先,我们来看一下 initProps
过程。
initProps
其实就做了两件事,遍历 props ,然后调用 defineReactive
方法把每一个 prop
对应的值变成响应式,可以通过 vm._props.xx
访问到定义 props
中对应的属性。然后我们看看 initData
做了什么。
遍历 data 上的值,先做合法性检测,然后调用 observe
来观察整个 data,很明显 observe
的内部还是会调用 defineReactive
方法的。
我们可以看到,observe
非常简单,如果没有 __ob__
属性,就实例化一个 Observer ,如果有就直接返回 ob 即可。所以问题关键是 Observer 中发生了什么。
Observer 的构造函数也很简单,实例化了一个 Dep 对象,然后把自身实例添加到对象 value 的 __ob__
的属性上,该属性不可枚举。最后判断 value ,如果是数组就调用 observeArray
方法,否则就调用 walk
方法。 因为 defineReactive
是对对象上的属性进行响应式,所以 walk 方法就是遍历分别对每一个属性执行 definedReactive
方法即可,而 observeArray
因为是 Array,里面的每一项可能是 object,所以是要分别执行 observe
方法。
我们接着来看一下 definedReactive
的实现,它的主要功能就是把对象中的属性变成响应式。
该方法先初始化了一个 Dep 的实例,接着拿到 obj
的属性描述符,然后对子对象递归调用 observe
方法,这样就保证了无论 obj
结构多复杂,都能够对其属性的每一项及其子属性变成响应式对象,这样我们访问或修改 obj
中一个嵌套层级比较深的属性,也能够触发 getter 和 setter。最后利用 Object.defineProperty
去给 obj
的属性 key
添加 getter 和 setter。其中 getter 做的就是依赖收集,setter 做的就是派发更新。