上次讲解,大体介绍了下目前的调试环境,若是没有看过的同窗能够看上一篇文章。为了弄明白咱们标题中所描述的问题,我先带你们看下vue中的响应式是怎样实现的。vue
首先来看一下咱们本次的代码案例:node
在DOM中定义了一个vue事例的data变量,2秒钟以后改变变量的字符串值。数组
在实例化vue的时候,会执行_init(options)方法。在_init方法中,涉及到了vue实例的初始化逻辑,在initState方法中运行了initData方法,通过一系列检查执行了 observe(data, true /* asRootData */)。在observe方法中,实例化了一个observer对象 ob = newObserver(value)。闭包
在实例化observer对象的中:app
给observer对象本身添加了一个dep属性,为Dep对象。ide
而后将observer对象自身挂载到传入的待观察value对象中,属性值为__ob__。函数
以后对传入的value对象执行了Observer原型上的walk方法,walk方法对value中的每一个属性执行了defineReactive方法 defineReactive(obj, keys[i])。defineReactive方法对每一个属性继续执行了observe方法,而后经过defineProperty继续对每一个属性设置get和set属性描述。spa
下面来重点讲解下defineReactive函数中的get。
调试
在刚进入defineReactive函数时实例化了一个dep实例。orm
在get函数中经过dep.depend作依赖收集。
对响应式对象的每一个属性执行defineReactive的时候,每一个属性都会有本身的dep实例。而且在get属性描述中执行了dep.depend,也就是在渲染Vnode时,只要读取了对象的响应式属性,就会执行这个属性的get函数。
Dep即dependence依赖,也就是把每一个对象的属性做为一个渲染Vue实例时的依赖。那么Vue实例咱们经过什么类来表示他和数据依赖的关系呢?就是Watcher,上次讲解到mountComponent中渲染和挂载时会实例化一个Watcher类,Watcher类中的操做其实就是把本身保存到一个全局的变量中,而且push到一个全局的栈中。以后来执行传入的回调函数来实现渲染Vnode和挂载。
在渲染Vnode的时候,就能够读取到响应式对象的属性,进而执行属性描述get函数。在get函数中,执行了保存在响应式属性闭包中dep实例的depend方法。此函数就是在刚才保存的全局watcher中添加当前的这个响应式属性闭包中的dep做为依赖,这就是依赖收集。而且在这个依赖属性的dep中加入了当前正在渲染的watcher。
接下来说defineReactive中的set属性描述,set函数中起到通知相应的vue实例更新的做用的函数就是dep.notify()函数。即更新这个dep中被订阅的全部watcher。
那么回归正题,在vue2对数组执行observe函数时又会发生什么?
在observe函数中检查了传入的参数是不是数组或对象,不是则直接返回。以后实例化observer对象。
在实例化Observer类时,如上图会先判断是不是数组,若是是数组,最后会执行observeArray函数,若是不是数组会执行walk函数。在walk函数中逐个属性执行defineReactive来定义set和get函数,可是observeArray函数中
则只是对每一个属性继续执行observe,并无对属性自己经过defineReactive函数来添加属性描述get或set。
下面来看一下若是咱们定义data为对象时,最后的data变量:
对比data为数组时的data变量:
在数组这一层中并无定义每一个元素的get和set属性,因此是不能直接用索引实现数组的响应式的。