当前位置: 首页 > news >正文

Vue2数组响应式问题:Object.defineProperty不能监听数组吗

一、官方文档说明

深入响应式原理 — Vue.js

Vue 不能检测以下数组的变动:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

举个例子:

var vm = new Vue({data: {items: ['a', 'b', 'c']}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将在响应式系统内触发状态更新:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

你也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:

vm.$set(vm.items, indexOfItem, newValue)

为了解决第二类问题,你可以使用 splice

vm.items.splice(newLength)

根据官网文档:

Vue 不能检测以下数组的变动:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

二、Object.defineProperty监听数组

我们都知道Vue2的响应式是基于Object.defineProperty劫持对象属性然后定义get和set去实现的,难道是Object.defineProperty无法监听数组?,实际如何我们代码见分晓:

// const arr = new Array(100).fill().map((i, index) => index + 1);

const arr = ['a', 'b', 'c', 'd'];

arr.forEach((i, index) => {

  Object.defineProperty(arr, index, {

    get() {

      console.log('get===>', i);

      return i;

    },

    set(newValue) {

      console.log('set===>', newValue);

      i = newValue;

    },

  });

});

arr[0];            // 打印:get===> a

arr[3];            // 打印:get===> d

arr[4];            // 没有触发监听

arr[0] = 99;    // 打印:set===> 99

arr[5] = 88;    // 没有触发监听

arr.push(88); // 没有触发监听

arr.pop();       // 打印:get===> d

arr.length = 1 // 没有触发监听

arr.shift();     

// 打印:

get===> a
get===> b
set===> b
get===> c
set===> c
get===> d
set===> d

arr.unshift(99);

// 打印:

get===> d
get===> c
set===> c
get===> b
set===> b
get===> a
set===> a
set===> 99

arr.splice(1)或arr.slice(1);

// 打印:
get===> b
get===> c
get===> d

说明:删除获取截取数组下标1及1后所有

arr.splice(4)或arr.slice(4); // 不触发监听

arr.splice(0, 1);

// 打印:

get===> a
get===> b
set===> b
get===> c
set===> c
get===> d
set===> d

说明:删除下标0,触发下标0get,后面元素往前补上导致索引变化索引触发get、set

arr.splice(0, 1, 99);

// 打印:

get===> a
set===> 99

说明:下标0变化,所以触发了下标0的get,set

 三、结论

由上面代码多方面验证,我们得出结论:Object.defineProperty是可以劫持并监听数组变化的,因为数组也是对象,它的属性就是数组下标,而因为Object.defineProperty只能劫持监听对象已有属性,所以对于arr[4]、arr[5] = 88、arr.push(88)、arr.splice(4)、arr.slice(4);这些没有超出数组长度、新增下标,没有改变数组原有元素下标的操作都不会触发监听

反之,如arr.splice(0, 1)、arr[0] = 99、arr.pop();、arr.unshift(99);这类修改数组原有元素或者破环数组下标顺序的操作会触发监听

四、Vue是如何实现数组的响应式的

那么由上面的结论得知Object.defineProperty是可以劫持数组并通过get、set监听原因元素变化的,但是Vue2并没有利用,因为:

  • 性能问题:数组可能非常大,为每个索引设置 getter 和 setter 会极大地影响性能。
  • 动态性问题:数组长度是动态变化的,每次数组变化时都需要重新为新的索引设置劫持,这在技术上是复杂且低效的。
  • length问题: 直接修改数组的 length 属性(例如,通过设置 arr.length = 0 来清空数组),这种操作同样无法被 Object.defineProperty 直接侦测到。这是因为 length 属性的变化不会触发索引属性的 setter。

正因为上述限制,Vue 2 选择了一种不同的方式来实现对数组的响应式监听:

重写数组方法:Vue 2 通过修改数组实例的原型,将数组的一些方法(如 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' 等)重写为可以触发视图更新的版本。当这些重写的方法被调用时,Vue 可以捕获到数组的变动并触发相应的更新。
总结来说,Object.defineProperty 由于其内在的机制和限制,并不能直接用于有效监听数组的变化。Vue 2 通过一种巧妙的方式绕过了这些限制,能够实现对数组操作的响应式更新。这种方法虽然巧妙,但有其局限性,比如直接通过索引设置数组元素的值或修改数组长度等操作,是无法被检测到的。

相关文章:

  • 论文略读:RegMix: Data Mixture as Regression for Language Model Pre-training
  • 杉山将(Sugiyama Masa)《图解机器学习》
  • 2023蓝桥杯C/C++ B组国赛
  • swagger通过配置将enum自动添加到字段说明中
  • Neo4j批量数据导入完全指南:高效处理大规模数据
  • Java多线程实现之同步方法详解
  • 创客匠人助力家庭教育IP破局:从0到1打造创始人个人品牌全攻略
  • Windows11下搭建Black Magic Probe (BMP) 编译环境
  • ESP32-s3 的I2C可以同时接LCD显示屏、IP5356M吗
  • c++ std::invoke
  • Docker Compose完整教程
  • 【Chipyard】 conda 环境安装与使用
  • 黑马python(四)
  • 正则表达式:开启文本处理的魔法之门
  • Git不能更新以及提交代码,提示链接超时,本地凭证无问题
  • Binder
  • ONLYOFFICE 协作空间 企业版使用秘籍-1.如何使用外部存储
  • 达梦数据库部署veri数据对比工具
  • 3.3.2 纠错编码(海明校验码)
  • 板凳-------Mysql cookbook学习 (十--5)
  • 湖南现在有什么网站做农副产品/百度竞价推广属于什么广告
  • wordpress taobaoke/石家庄seo网站排名
  • 深圳购物网站建设/营销策略怎么写范文
  • wordpress小说网站主题/宁德市医院东侨院区
  • 021新手学做网站/如何在各大网站发布信息
  • 做化妆品网站的意义/网店搜索引擎优化的方法