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

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 通过一种巧妙的方式绕过了这些限制,能够实现对数组操作的响应式更新。这种方法虽然巧妙,但有其局限性,比如直接通过索引设置数组元素的值或修改数组长度等操作,是无法被检测到的。

http://www.xdnf.cn/news/13525.html

相关文章:

  • ES Modules 与 CommonJS 的核心区别详解
  • python的时间管理库whenever的使用
  • Office2019下载安装教程(2025最新永久方法)(附安装包)
  • 【Vue】组件及组件化, 组件生命周期
  • 【AI大模型入门指南】概念与专有名词详解 (二)
  • CSP-J 2020 入门级 第一轮 阅读程序(1)
  • 【Zephyr 系列 19】打造 BLE 模块完整 SDK:AT 命令系统 + 状态机 + NVS + OTA 一体化构建
  • 华为云Flexus+DeepSeek征文 | 基于Dify构建多语言文件翻译工作流
  • NIFI在Linux系统中的系统配置最佳实践(性能调优)
  • UE5 读取配置文件
  • 【笔记】代码开发中常用环境配置与好用工具
  • Android12 开机后桌面加载框的适配
  • 拼音分词器的配置
  • kubernetes--通俗理解Sidecar容器
  • WinHex 20.8-SR1 安装教程详细步骤+下载
  • 【AI大模型入门指南】概念与专有名词详解 (一)
  • 【算法篇】逐步理解动态规划模型6(回文串问题)
  • RabbitMQ可靠和延迟队列
  • 2025下半年软考系统分析师备考攻略:6个月通关计划与高频考点解析
  • CBAM认证概述,CBAM认证的核心要素,CBAM认证的未来发展
  • 力扣HOT100之堆:295. 数据流的中位数
  • 分类数据集 - 植物分类数据集下载
  • 【C++】浅谈C++多态
  • vue3 + ant 实现 tree默认展开,筛选对应数据打开,简单~直接cv
  • Linux 引导过程与服务控制
  • TBvision 静态测试以及生成报告教程
  • GlusterFS 分布式文件系统
  • 【笔记】NVIDIA AI Workbench 中 sudo 密码问题排查与解决
  • 电流传感器在汽车中的应用:从BMS电池管理到电机控制的工程解析
  • 纯血Harmony NETX 5小游戏实践:趣味三消游戏(附源文件)