前言:

众所周知,Vue2.0 对于数据响应式的实现上是有一些局限性的,比如:

  • 无法检测数组和对象的新增
  • 无法检测通过索引改变数组的操作

针对以上问题,我们一般都会把锅甩给 Object.defineProperty。所以,在Vue 3.0 中,尤大把响应式数据部分弃用了 Object.defineProperty,而使用 Proxy 来代替它。

难道 Object.defineProperty 真的要背这锅么,下面就来分析一下 Object.defineProperty 真的无法监测数组下标的变化吗?

一、Object.defineProperty:

首先,Object.defineProperty 本身是可以监控到数组下标的变化的,只是在 Vue 的实现中,从性能/体验的性价比考虑,便放弃了这个特性。

我们可以通过以下例子,对遍历数组中的每一项,用 Object.defineProperty 对其进行监测:

function defineReactive(data, key, value) {
	 Object.defineProperty(data, key, {
		 enumerable: true,
		 configurable: true,
		 get: function defineGet() {
			 console.log(`get key: ${key} value: ${value}`)
			 return value
		 },
		 set: function defineSet(newVal) {
			 console.log(`set key: ${key} value: ${newVal}`)
			 value = newVal
		 }
	 })
}
 
function observe(data) {
	Object.keys(data).forEach(function(key) {
		defineReactive(data, key, data[key])
	})
}
 
let arr = [1, 2, 3]
observe(arr)

运行结果:
在这里插入图片描述
可以看出:我们通过索引改变 arr[1],可以发现触发了 set,也就是Object.defineProperty 是可以检测到通过索引改变数组的操作的。

同样的,Vue 对数组的7个操作方法(pushpopshiftunshiftsplicesortreverse)也实现了响应式,这里我们就不做测试了,感兴趣的小伙伴可以自己试一下。

总结:

Object.defineProperty 在数组中的表现和在对象中的表现是一致的,数组的索引就可以看做是对象中的 key 。

通过使用上面操作数组的方法发现:

  • 通过索引访问或设置对应元素的值时,可以触发 gettersetter 方法
  • 通过 pushunshift 会增加索引,对于新增加的属性,需要再手动初始化才能被 observe
  • 通过 popshift 删除元素,会删除并更新索引,也会触发 settergetter 方法

所以,Object.defineProperty 是有监控数组下标变化的能力的,只是在 vue2.x 放弃了这个特性。


二、Vue2.0 不能检测数组和对象的变化原因?

我们也看了上面 Object.defineProperty 的讲解,明白了Vue2.0 不能检测数组和对象的变化,并不是JavaScript的锅,也更不是 Object.defineProperty 的锅了。
在这里插入图片描述

尤大也说的很清楚了,在 Vue 的实现中,从性能/体验的性价比考虑,放弃了这个特性。

当然,在 Vue3.0 中则对这方面做了很大程度的改进,采用 Proxy 替代了 Object.defineProperty,那么,Proxy 可以完美解决这个问题吗?Object.definePropertyProxy 他们之间又有什么区别呢,具体关于 Proxy 的相关内容,我们下篇文章来一起研究研究~


三、Vue2.0 不能检测数组和对象的解决方案?

关于这个问题,我们可以拆分开来讨论,同时针对vue能够监听的场景和无法监听的场景进行讨论,并给出对应的解决方案:

监听数组的变化:

1)vue 能够监听数组变化的场景
  1. 通过赋值的形式改变正在被监听的数组;
  2. 通过 splice(index, num, val) 的形式改变正在被监听的数组;
  3. 通过数组的 push 的形式改变正在被监听的数组;
2)vue 无法监听数组变化的场景
  1. 通过数组索引改变数组元素的值;
  2. 改变数组的长度;
3)vue 无法监听数组变化的解决方案
  1. this.$set(arr, index, newVal)
  2. 通过 splice(index,num,val)
  3. 使用临时变量作为中转,重新赋值数组;

监听对象的变化:

1)vue 能够监听对象变化的场景
  1. 通过直接赋值的场景;
    e.g:watchObj = {name:"zyk"}
2)vue 无法监听对象变化的场景
  1. 对象的增加、删除、修改无法被vue监听到
3)vue 无法监听对象变化的解决方案
  1. 使用 this.$set(object, key, value)(vue 无法监听 this.$set 修改原有属性)

  2. 使用 Object.assign(),直接赋值的原理;(深拷贝,推荐使用)
    关于 Object.assign() — MDN 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。

    其实使用 Object.assign() 也是基于深拷贝的实现原理,如果大家对深拷贝不太清楚,请移步至:浅拷贝 VS 深拷贝,并且手写一个深拷贝(深克隆)


经过上面的分析总结,我想大家应该对 Vue2.0 为什么不能检测数组和对象的变化以及对应的解决方案有了直接的认识。

希望大家能在开发过程中可以完美解决任何问题。

如果大家有什么更好的解决方案,欢迎留言讨论。

最后不要忘记一键三连哦~各位!加油!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐