关于Vue 监控数组的问题

 更新时间:2022年05月28日 10:24:33   作者:拜小白  
这篇文章主要介绍了Vue 监控数组的示例,主要包括Vue 是如何追踪数据发生变化,Vue 如何更新数组以及为什么有些数组的数据变更不能被 Vue 监测到,对vue监控数组知识是面试比较常见的问题,感兴趣的朋友一起看看吧

常见面试题

  • Vue 如何监控数组
  • defineProperty 真的不能监测数组变化吗?

Vue 是如何追踪数据发生变化

在 Vue 中当我们把一个普通的 JS 对象作为 data 传入 Vue 实例,Vue2.x 对这个数据初始化时将遍历这个对象所有的属性,并使用 JS 的原生特性 Object.defineProperty 把这些属性全部转为 getter\setter。这些 getter\setter 对用户来说是不可见的,他们可以在属性被访问和修改时通知变更。同时每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据属性记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

Vue 如何更新数组

// 方法一: 使用 Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// 方法二: 使用 Vue 可监测的数组变异方法: Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

为什么有些数组的数据变更不能被 Vue 监测到

简单来说,我们操作数组的一些动作 arr[2] = 'xxx' / arr.length = 2 或者是调用 Array.prototype 上挂载的部分方法并不能触发这个属性的 setter。

在数组的更新中有提到,可以使用 Vue 可监测的数组变异方法: Array.prototype.splice, 哪为什么这个方法可以触发状态的更新了。 这是因为 Vue2.x 将数组的 7 个常用方法 push、pop、shift、unshift、splice、sort、reverse 进行了重写,所以通过调用包装之后的数组方法就能够被 Vue 监测到。

// Vue 2.6.14 
// src/core/observer/array.js
import { def } from '../util/index'
 
// 记录原始 Array 未重写之前的 API 原型方法
const arrayProto = Array.prototype
// 深拷贝一份上面的原型出来
export const arrayMethods = Object.create(arrayProto)
 
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
 
/**
 * Intercept mutating methods and emit events
 * 拦截上边数组中列出的变异方法, 并发出事件通知
 */
methodsToPatch.forEach(function (method) {
  // cache original method
  // 缓存 Array.prototype 中的同名原始方法
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    // 调用执行原有的数组方法
    const result = original.apply(this, args)
    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
  })
})

Vue 为什么不能通过下标操作数组或者改变数组的长度来触发视图更新

那 Vue2.x 监测数组变更的两条限制:不能监听利用索引直接设置一个数组项,不能监听直接修改数组的长度,是因为 defineProperty 的限制么?

答案:是的

Object.defineProperty 对于数组变化监听的表现与 Vue2.x 还是有不同的,比如 Object.defineProperty 可以监听到通过索引直接修改数组项,当然也不是说 Object.defineProperty 可以完全监听数组的变化,像直接修改数组的长度或者 push\pop 之类的方法还是不能触发 setter 的。

这里就会出现一个新的问题?

为什么 Object.defineProperty 明明能监听到数组值的变化,而 Vue 却没有实现呢?

这是因为 Vue 是对数组元素进行了监听,而没有对数组本身的变化进行监听。

var Observer = function Observer (value) {
    this.value = value;
    this.dep = new Dep();
    this.vmCount = 0;
    def(value, '__ob__', this);
    // 区分对象和数组,对象和数组走不通的响应式方案
    if (Array.isArray(value)) {
      // 判断是否支持__proto__属性,根据不同的请求来添加数组的拦截方法
      if (hasProto) {
        protoAugment(value, arrayMethods);
      } else {
        copyAugment(value, arrayMethods, arrayKeys);
      }
      // 循环数组的元素,再次调用observe方法,
      this.observeArray(value);
    } else {
      // 如果是对象,循环对象属性,为对象属性添加getter,setter方法,将属性变成响应式
      this.walk(value);
    }
  };

这其实是出于性能原因的考量,给每一个数组元素绑定上监听,实际消耗很大而受益并不大

其实还有一些考虑是:对数据的操作更常用的操作数组的方法是使用数组原型上的一些方法如 push、shift 等来操作数组。Object.defineProperty是对象上的方法,用来对数组的下标进行检测,会改变数据本来的性质。

总结来说:三点原因

  • 性能原因的考量
  • 对数据的操作更常用的操作数组的方法是使用数组原型上的一些方法如 push、shift 等来操作数组。
  • Object.defineProperty是对象上的方法,用来对数组的下标进行检测,会改变数据本来的性质。

当然最重要的就是性能问题。

Vue 3.0 是如何处理的?

Vue3 不再采用 defineProperty 的方式来进行监听而是采用 Proxy 的方式。下面我引用了 MDN 上对于 proxy 的介绍: Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。 当异步触发 Model 里的数据变化时,都会经过 Proxy 这一层,在这里则可以监听数组以及各种数据类型的变化,无论是数组下标赋值引起变化还是数组方法引起变化,都可以被监听到,也可以避开监听数组每个属性下造成的性能问题。

参考

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

相关文章

  • Vue3使用hook封装媒体查询和事件监听的代码示例

    Vue3使用hook封装媒体查询和事件监听的代码示例

    这篇文章主要给大家详细介绍Vue3如何使用hook封装媒体查询和事件监听,使得Vue的开发更加丝滑,文中通过代码示例给大家介绍的非常详细,感兴趣的同学跟着小编一起来学习吧
    2023-07-07
  • vue如何实现拖动图片进行排序Vue.Draggable

    vue如何实现拖动图片进行排序Vue.Draggable

    这篇文章主要介绍了vue如何实现拖动图片进行排序Vue.Draggable,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • VUE使用echarts 5.0以上版本渲染器未导入错误问题

    VUE使用echarts 5.0以上版本渲染器未导入错误问题

    这篇文章主要介绍了VUE使用echarts 5.0以上版本渲染器未导入错误问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • element-ui直接在表格中点击单元格编辑

    element-ui直接在表格中点击单元格编辑

    最近通过在网上查找资料,自己整合研究了一个可以直接在表格中操作数据的基于element-ui的前端模板。可以让增改数据的操作显得方便一点,感兴趣的可以了解一下
    2021-12-12
  • Vue Element前端应用开发之界面语言国际化

    Vue Element前端应用开发之界面语言国际化

    我们开发的系统,一般可以不用考虑语言国际化的问题,大多数系统一般是给本国人使用的,而且直接使用中文开发界面会更加迅速 一些,不过框架最好能够支持国际化的处理,以便在需要的时候,可以花点时间来实现多语言切换的处理,使系统具有更广泛的受众用户。
    2021-05-05
  • vue router导航守卫(router.beforeEach())的使用详解

    vue router导航守卫(router.beforeEach())的使用详解

    导航守卫主要用来通过跳转或取消的方式守卫导航。这篇文章主要介绍了vue-router导航守卫(router.beforeEach())的使用,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-04-04
  • vue2.0实现点击其他区域关闭自定义div功能

    vue2.0实现点击其他区域关闭自定义div功能

    这篇文章主要介绍了vue2.0实现点击其他区域关闭自定义div功能实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • Vue实现购物车功能

    Vue实现购物车功能

    本篇文章主要分享了Vue实现购物车功能的实例代码。具有很好的参考价值。下面跟着小编一起来看下吧
    2017-04-04
  • 详解Vue.js自定义tipOnce指令用法实例

    详解Vue.js自定义tipOnce指令用法实例

    这篇文章主要介绍了详解Vue.js自定义tipOnce指令用法实例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • vue框架下部署上线后刷新报404问题的解决方案(推荐)

    vue框架下部署上线后刷新报404问题的解决方案(推荐)

    这篇文章主要介绍了vue框架下部署上线后刷新报404问题解决方案,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-04-04

最新评论