详解Vue2如何监听数组的变化

 更新时间:2024年03月20日 15:16:11   作者:接着奏乐接着舞。  
这篇文章主要来和大家详细探讨一下Vue2中是如何监听数组的变化的,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

前言

众所周知,vue2的响应式原理是 数据劫持结合发布订阅模式.具体是通过Object.defineProperty()方法来劫持各个属性的getter和setter,从而能够监听到数据的变化。,但是Object.defineProperty不能监听数组的变化那么vue2是怎么实现数组响应式的呢?而且在日常开发中,我们会发现不能直接修改数组的length长度,也不能通过数组下标的方式修改数据,比如:arr[0]=123这种方式不能响应式。

那么,vue2是如何实现的呢?

Vue2内部通过重写数组的原型方法来监听数组的变动

具体来说,Vue2首先获取到数组的原型,然后创建一个新的对象继承自该原型,接着将这个新对象的原型上的七个能够修改数组自身的方法(push、pop、shift、unshift、splice、sort、reverse)进行重写。这些方法在执行时,除了执行其原有的逻辑之外,还会触发视图更新。

以下是一个简化的重写示例:

// 获取数组的原型
const arrayProto = Array.prototype;
// 创建一个新的对象,该对象的原型就是arrayProto
const arrayMethods = Object.create(arrayProto);

// 需要被改写的方法
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
];

methodsToPatch.forEach(function(method) {
  // 缓存原始方法
  const original = arrayProto[method];
  // 定义新的方法
  Object.defineProperty(arrayMethods, method, {
    value: function mutator(...args) {
      // 先执行原始方法
      const result = original.apply(this, args);
      // 获取数组对象的__ob__属性,__ob__是每个响应式对象都有的一个属性,指向该对象的Observer实例
      const ob = this.__ob__;
      // 如果方法是新增元素的操作,将新增的元素转换为响应式
      let inserted;
      switch (method) {
        case 'push':
        case 'unshift':
          inserted = args;
          break;
        case 'splice':
          inserted = args.slice(2);
          break;
      }
      if (inserted) ob.observeArray(inserted);
      // 通知变更
      ob.dep.notify();
      return result;
    },
    configurable: true,
    enumerable: false,
    writable: true
  });
});

如何使用

在初始化响应式数据时,Vue会判断一个对象是否是数组。如果是数组,Vue则会将这个数组的原型指向上面提到的arrayMethods,从而使得这个数组调用7个修改自身的方法时,能够触发视图的更新。这一过程主要是在Observer类的实例化过程中完成的。

if (Array.isArray(value)) {
  if (hasProto) {
    protoAugment(value, arrayMethods);
  } else {
    copyAugment(value, arrayMethods, arrayKeys);
  }
  this.observeArray(value);
}

小结

通过这种方式,Vue 2可以监测到数组的变化并作出响应。这种方法虽然巧妙,但有其局限性,比如直接通过索引设置数组元素的值或修改数组长度等操作,是无法被检测到的。Vue 3中采用了Proxy代替了这种实现方式,能够更好地解决这些问题。

解决方式

虽然Object.defineProperty本身无法拦截数组索引的直接修改或数组长度的变化,Vue 2提供了几种方法来解决这个限制,确保开发者仍然可以以响应式的方式更新数组:

使用Vue.set 或 vm.$set

为了解决直接通过索引修改数组元素的问题,Vue 2引入了Vue.set函数和vm.$set实例方法。这两个方法允许开发者在指定索引处插入或替换数组元素,同时保证变化是响应式的。

// 假设有一个Vue组件的data如下:
data() {
  return {
    fruits: ['apple', 'banana', 'cherry']
  };
},
methods: {
  updateFruit() {
    this.$set(this.fruits, 1, 'orange'); // 将索引1处的'banana'替换为'orange'
  }
}

使用数组的splice方法

另一个解决方案是使用数组的splice方法。splice不仅可以在数组中添加/删除项目,而且由于Vue重写了这个方法,使用它进行的任何操作都会触发视图更新。

updateFruit() {
  this.fruits.splice(1, 1, 'orange'); // 同样的效果,替换操作
}

响应式系统的限制与规避策略

虽然Vue的响应式系统提供了强大的数据绑定能力,但了解其内部工作原理和限制对于开发高效、可维护的Vue应用至关重要。通过正确地使用Vue提供的工具和方法(如Vue.set、vm.$set和splice),开发者可以确保即使是那些原生JavaScript限制下不可直接侦测的变化,也能被Vue的响应式系统捕获并正确地更新视图。

面试题

Object.defineProperty如何监听数组?为什么无法获取数组的变化?

Object.defineProperty 本身并不直接用于监听数组的变化,因为它是设计来劫持和监听对象属性的读取和写入操作的。当我们使用 Object.defineProperty 对对象的属性进行劫持时,我们实际上是在设置属性的 getter 和 setter,这样每当属性被访问或修改时,我们就可以执行自定义的逻辑,比如通知视图进行更新。然而,当应用到数组上时,存在几个核心限制使得 Object.defineProperty 无法有效地监听数组的变化:

1. 数组索引的修改

当通过索引直接修改数组(如 arr[0] = 'new value')时,这实际上是一个属性赋值操作。虽然理论上可以对数组的每个索引使用 Object.defineProperty 来监听变化,但这在实践中是不可行的,因为:

性能问题:数组可能非常大,为每个索引设置 getter 和 setter 会极大地影响性能。

动态性问题:数组长度是动态变化的,每次数组变化时都需要重新为新的索引设置劫持,这在技术上是复杂且低效的。

2. 修改数组长度

直接修改数组的 length 属性(例如,通过设置 arr.length = 0 来清空数组),这种操作同样无法被 Object.defineProperty 直接侦测到。这是因为 length 属性的变化不会触发索引属性的 setter。

3. 使用数组方法

数组的方法(如 push、pop、splice 等)可以修改数组的内容或结构。这些操作不仅改变数组元素,有时还会改变数组的长度。Object.defineProperty 无法直接拦截这些方法调用,因为它们是数组原型上的方法,而不是数组实例上的直接属性。

Vue 2 如何实现数组的响应式

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

重写数组方法:Vue 2 通过修改数组实例的原型,将数组的一些方法(如 push、pop 等)重写为可以触发视图更新的版本。当这些重写的方法被调用时,Vue 可以捕获到数组的变动并触发相应的更新。

总结来说,Object.defineProperty 由于其内在的机制和限制,并不能直接用于有效监听数组的变化。Vue 2 通过一种巧妙的方式绕过了这些限制,能够实现对数组操作的响应式更新。

到此这篇关于详解Vue2如何监听数组的变化的文章就介绍到这了,更多相关Vue2监听数组变化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue2利用html2canvas+jspdf动态生成多页PDF方式

    vue2利用html2canvas+jspdf动态生成多页PDF方式

    利用vue2结合html2canvas和jspdf,可以实现前端页面内容导出为PDF的功能,首先需要安装相关依赖,使用html2canvas将指定div内容捕捉为图像,再通过jspdf将图像转换为PDF
    2024-09-09
  • vue中keep-alive、activated的探讨和使用详解

    vue中keep-alive、activated的探讨和使用详解

    这篇文章主要介绍了vue中keep-alive、activated的探讨和使用详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07
  • vite vue3 路由配置@找不到文件的问题及解决

    vite vue3 路由配置@找不到文件的问题及解决

    在Vite项目中配置路由时,可能会遇到文件路径错误导致的加载失败问题,常见的解决办法包括安装路径处理插件、正确设置vite.config.js中的路径别名以及重启项目,通过正确配置,可以确保路由正确加载对应的界面文件,避免路径错误导致的问题
    2024-10-10
  • 深入理解vue $refs的基本用法

    深入理解vue $refs的基本用法

    本篇文章主要介绍了深入理解vue $refs的基本用法 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • vue-video-player视频播放器使用配置详解

    vue-video-player视频播放器使用配置详解

    这篇文章主要为大家详细介绍了vue-video-player视频播放器的使用和配置,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-10-10
  • elementui中树形表格切换展开不同层级的示例代码

    elementui中树形表格切换展开不同层级的示例代码

    这篇文章主要介绍了elementui中树形表格切换展开不同层级,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08
  • vue下拉列表功能实例代码

    vue下拉列表功能实例代码

    这篇文章主要介绍了vue下拉列表功能实例代码,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2018-04-04
  • 如何根据业务封装自己的功能组件

    如何根据业务封装自己的功能组件

    这篇文章主要介绍了Vue封装功能组件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • vue el-table实现行内编辑功能

    vue el-table实现行内编辑功能

    这篇文章主要为大家详细介绍了vue el-table实现行内编辑功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • Vue实现商品飞入购物车效果(电商项目)

    Vue实现商品飞入购物车效果(电商项目)

    这篇文章主要介绍了Vue实现商品飞入购物车效果(电商项目),这种效果在一些电商平台经常会用到,今天小编通过代码详解,需要的朋友可以参考下
    2019-11-11

最新评论