🔥🔥Vue2.0为何不能检查数组的变化?又该如何解决?


image.png

这是我参与更文挑战的第7天,活动详情查看: 更文挑战html


前言

咱们都知道,Vue2.0对于响应式数据的实现有一些不足:vue

  • 没法检测数组/对象的新增
  • 没法检测经过索引改变数组的操做。

分析

  • 没法检测数组/对象的新增?git

    Vue检测数据的变更是经过Object.defineProperty实现的,因此没法监听数组的添加操做是能够理解的,由于是在构造函数中就已经为全部属性作了这个检测绑定操做。github

  • 没法检测经过索引改变数组的操做。即vm.items[indexOfItem] = newValue?web

    官方文档中对于这两点都是简要的归纳为“因为JavaScript的限制”没法实现,而Object.defineProperty是实现检测数据改变的方案,那这个限制是指Object.defineProperty吗?数组

思考

vm.items[indexOfItem] = newValue真的不能被监听么?

Vue对数组的7个变异方法(push、pop、shift、unshift、splice、sort、reverse)实现了响应式。这里就不作测试了。咱们测试一下经过索引改变数组的操做,能不能被监听到。markdown

遍历数组,用Object.defineProperty对每一项进行监测ide

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)
复制代码

image-20210607020953993

测试说明函数

经过索引改变arr[1],咱们发现触发了set,也就是Object.defineProperty是能够检测到经过索引改变数组的操做的,那Vue2.0为何没有实现呢?是尤大能力不行?这确定毋庸置疑。那他为何不实现呢?oop

后续

后来我在网上找的时候发现了这个。🌀🔥这是github上,一位开发小哥对尤大提到问题。

image-20210607021557820


小结:原来是出于对性能缘由的考虑,没有去实现它。而不是不能实现。

对于对象而言,每一次的数据变动都会对对象的属性进行一次枚举,通常对象自己的属性数量有限,因此对于遍历枚举等方式产生的性能损耗能够忽略不计,可是对于数组而言呢?数组包含的元素量是可能达到成千上万,假设对于每一次数组元素的更新都触发了枚举/遍历,其带来的性能损耗将与得到的用户体验不成正比,故vue没法检测数组的变更。

不过Vue3.0用proxy代替了defineProperty以后就解决了这个问题。


解决方案

数组
  1. ​ this.$set(array, index, data)

    //这是个深度的修改,某些状况下可能致使你不但愿的结果,所以最好仍是慎用
    this.dataArr = this.originArr
    this.$set(this.dataArr, 0, {data: '修改第一个元素'})
    console.log(this.dataArr)        
    console.log(this.originArr)  //一样的 源数组也会被修改 在某些状况下会致使你不但愿的结果 
    复制代码
  2. splice

    //由于splice会被监听有响应式,而splice又能够作到增删改。
    复制代码
  3. 利用临时变量进行中转

    let tempArr = [...this.targetArr]
    tempArr[0] = {data: 'test'}
    this.targetArr = tempArr
    复制代码
对象
  1. this.$set(obj, key ,value) - 可实现增、改

  2. watch时添加deep:true深度监听,只能监听到属性值的变化,新增、删除属性没法监听

    this.$watch('blog', this.getCatalog, {
        deep: true
        // immediate: true // 是否第一次触发
      });
    复制代码
  3. watch时直接监听某个key

    watch: {
      'obj.name'(curVal, oldVal) {
        // TODO
      }
    }
    复制代码

🔥最后:你们有什么不一样的看法,欢迎留言讨论。要是以为不错,给小攻城狮点个赞呗👀

相关文章
相关标签/搜索