这就要从reactive开始讲起了,粗略的说,js的操做能引发页面上显示的改变,是由于该操做引发了组件的从新渲染,渲染会生成新的虚拟节点,新节点和旧节点会对比,操做浏览器的node进行改变。vue
vue实例如下简称vmnode
说具体点react
data()方法的返回值以及watch,props,computed里面的属性,,在这以前都会被defineReactive方法重写get和set方法(get方法重写为调用的时候收集Dep这个静态变量指向的watcher,set方法调用的时候会notify收集的watchers,调用每一个watcher的update--->run--->get方法)和methods里面的方法一块儿,被挂载到vm实例上(因此methods里面this.XXX才有用,由于调用method的是 vm,属性也在vm上)数组
-----------------因此若是想要对象(好比a)的属性b的set方法能引发从新渲染,必须在b被get的时候,收集到能够引发从新渲染的watcher。那么是何时收集的呢?浏览器
每个vm有一个render Watcher,在mountComponent的时候生成,render Watcher的expOrFn方法是 vm._update(vm._render(), hydrating)--->vm.__patch__(prevVnode, vnode),(对比新旧虚拟节点,操做真实node),在这个过程当中,vm._render()会按照Vue的<template>中的内容,为每个子节点this
生成属性,查看render的代码会发现有个with(this),这里this就是vm,就是要调用vm的属性的get方法给虚拟节点的属性赋值。而后再进行对比等操做。在这个过程当中,被调用get方法的属性就会收集renderWatcher。server
-----------------那为何array[0]='a'这样的方法不会从新渲染?对象
由于array的0这个属性?,没有重写get和set方法递归
-----------------为何pop push能够reactive?get
好比a.b=c,reactive的过程是: observe(a)---> defineReactive(b)--->重写b的get方法以前先observe(c)。observe c的时候,若是判断c是一个数组,那么c的pop.、push等方法会从新指向,指向后的新方法,先调用Array原来来对应的方法,而后调用_ob_指向的observer的dep的notify方法,通知收集的watcher
-----------------对象的dep从哪里来?
上面说过,observe(c),new一个Observer 实例childOb,c的_ob_属性指向childOb,childOb里面有一个dep属性,而在b的重写的get方法中,会有childOb.dep.depend()的执行
-----------------灵魂第五问:既然是给b重写set和get的时候,给c的_ob_的dep收集Watcher,那么数组里面的元素好比array[0]=d,而d刚好是数组,为何d.push也能够reactive?
在childOb.dep.depend()方法下面还有个if,若是属性指向的是个数组,直接递归收集。那么这时候若是是数组的元素是个对象,就无论了,只考虑数组 中的元素仍是数组的状况。因此array[0]='a'是不能reactive的,可是array[0].push('a')是能够reactive的