Vue2.x如何实现数据劫持
* 我们在使用vue框架的时候,在创建一个vue实例对象的时候需要传入一个配置对象,其中data节点存放了该组件的数据状态,而我们在使用或者修改data里的数据时候可以直接通过组件本身this去拿到数据进行操作,那么vue框架是如何把data中的数据代理到组件上以及监听到数据的变化呢?
利用Object.defineProperty代理数据
Vue构造函数接受一个配置对象options,该options就是我们在创建组件时传入的配置对象。然后在Vue的prototype上定义一个用于初始化函数,其中把options赋值给vue实例上的属性$options,并且调用一个函数初始化状态。
在new Vue的时候将会调用Vue原型链上的初始化方法,并且把options传递到函数中。
初始状态函数中接受vue实例对象,并且判断组件上是否存在data节点,如果存在就调用initdata去初始化数据。因为data有可能是函数或者对象,所以利用typeof进行判断,如果是函数就调用并且定义一个变量与vue实例下的_data去接收返回的对象, 如果是对象就直接赋值。
遍历data属性,使用Object.defineProperty把vm._data数据代理到vue实例对象下 利用递归与Object.definePrototype深度监听data中的数据
因为data中的数据有可能是引用类型的数据,所以使用递归,逐层判断数据类型。
定义一个observe函数,判断data是否是对象类型的数据,注意:这里要排除掉null这种情况.
但因为对象和数组都属于引用类型的数据,所以需要分两种情况。当是数据是一个对象时,仍然是遍历数据中的属性,取到对应的值去使用Object.definePrototype监听数据的变化,但对应的值也有可能是个对象,所以在监听对应的值之前调用observe再次监听该值,并且在设置该属性对应的值的时候,也要调用observe去监听重新赋的值,因为重新赋的值也会可能是一个引用类型数据。
当数据是一个数组时,就无法利用Object.definePrototype去监听了,那么就需要重写那些能够修改元素数组的Array原型链上的方法了
- 将需要重写的方法枚举出来,例如 push、 pop、shift、unshift
- 自定义一个原型对象,使用Object.create()去继承原有的数组原型,这是因为一些不改变原数组的方法仍然需要
- 遍历需要重写的函数名,给自定义的原型对象加上对应的方法,在定义对应方法上,传入的参数个数不一定 ,使用arguments接收参数, 把伪数组转成数组 ,目的是可以调用apply来调用真正的数组方法
- 利用apply调用实际的数组方法 ,即是Array原型上的方法, 调用该函数是要监听的data中的数组 所以apply中的参数就是监听的数组
- 判断调用的是数组中的哪个函数,如果数组调用了一些添加元素的方法,把新增的元素组成数组,然后遍历其中元素, 判断新增的元素是不是对象元素,进行深层监听