响应式原理
什么是响应式
- 顾名思义,监测变化,做出响应。
监测变化
- Object是JavaScript中的一种数据类型,是引用类型
- 对于Object属性的变化,JavaScript提供了Object.defineProperty和Proxy来监测
- Vue2的响应式就是通过Object.defineProperty来监测的
- Object.defineProperty( obj, prop, descriptor )
- 这个方法的具体用法参考MDN
- 该方法每次只能观测对象的其中一个prop
- 该方法只能观测对象已有的prop
- Vue2是如何实现data配置项的响应式的
- 获取到data,有可能是对象或者返回对象的函数
- 拿到最终的data对象,然后遍历对象的每个prop,调用Object.defineProperty去监测变化
做出响应
- 简单的两个问题:对于监测到的变化,谁要去处理,怎么处理
- 对于Vue2框架本身,监测到data的属性变化后,所有用到该属性的模板都要重新渲染。
- 即Vue2框架自身去处理,调用渲染函数重新渲染页面来处理
完善响应式
-
到现在为止,对于data对象属性的响应式还有几点不足
- 目前实现的只是对于data对象本身属性的侦测,且属性必须是基本类型,而不能是引用类型
- 把上述过程封装为一个函数,则该函数的功能就是实现对一个对象的所有基本属性的侦测和响应
-
针对以上问题依次解决
- 深层响应:对于属性也为对象时,要深层监视变化,则可以通过嵌套调用封装的监视变化的函数
- 数组属性:如果属性是数组类型,由于Array本身没有提供方法来监测元素的变化,因此Vue2通过代理模式,将原有的操作数组元素的方法重写,这样在重写的方法中就能在其每次被调用时监测变化。
注意点
- 目前Vue2的响应式只支持data对象中属性类型为基本类型,对象,数组
- 对于数组也必须使用七个操作数组元素的方法,而不能通过下标来操作
- Object.defineProperty只能观察data对象中显式存在的属性,新增的属性,没有经过defineProperty,没有响应式
- 删除的属性,由于在删除前其已经存在,即已经在defineProperty监测中了,而delete动作不会被捕获到,值没有发生变化,只是不存在了为undefined
扩展
- 如果把对data对象的响应式的实现过程封装一遍,那就可以对任意的对象进行同等功能的监测
- 比如$watch就是简单封装,进行了部分改动,实现了对任意对象的监测
- 与Vue框架本身的响应式不同的是,$watch让我们可以自定义监测变化后要做出的响应,而不是Vue自动重渲染
- 对已经监测过的对象新增和删除属性实现响应式,
- 与数组的变化侦测思想一致,通过代理模式
- 创建delete方法,在方法内去调用原生的delete,和点语法新增。
- 由于用户调用的是我们创建的delete方法,因此我们能监测到这个变化的动作,当变化后,我们可以手动调用defineProperty来对新增的属性进行监测
- set同理