关于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  监控数组内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue项目引入Iconfont图标库的教程图解

    vue项目引入Iconfont图标库的教程图解

    这篇文章主要介绍了vue项目引入Iconfont图标库的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-10-10
  • Vue实现简单的拖拽效果

    Vue实现简单的拖拽效果

    这篇文章主要为大家详细介绍了Vue实现简单的拖拽效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08
  • Vue打包程序部署到Nginx 点击跳转404问题

    Vue打包程序部署到Nginx 点击跳转404问题

    这篇文章主要介绍了Vue打包程序部署到Nginx 点击跳转404问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • Vue获取页面元素的相对位置的方法示例

    Vue获取页面元素的相对位置的方法示例

    这篇文章主要介绍了Vue获取页面元素的相对位置的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • vue数据更新了但在页面上没有显示出来的解决方法

    vue数据更新了但在页面上没有显示出来的解决方法

    有时候 vue 无法监听到数据的变化,导致数据变化但是视图没有变化,也就是数据更新了,但在页面上没有显示出来,所以本文给出了三种解决方法,通过代码示例介绍的非常详细,需要的朋友可以参考下
    2023-12-12
  • vue+vite+diff.js使用小结

    vue+vite+diff.js使用小结

    本文主要介绍了vue+vite+diff.js使用小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-12-12
  • 深入探索Vue3.x中的七大高级用法

    深入探索Vue3.x中的七大高级用法

    Vue3.x 自发布以来,凭借其性能的显著提升和更加灵活的组合式 API,已经成为了现代前端开发的重要工具之一,除了基本用法外,Vue3.x 还提供了许多高级功能,本文将和大家一起深入探索Vue3.x中的七大高级用法,需要的朋友可以参考下
    2024-03-03
  • Vue前端框架搭建过程

    Vue前端框架搭建过程

    这篇文章主要介绍了Vue前端框架搭建过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08
  • 如何使用vue实现跨域访问第三方http请求

    如何使用vue实现跨域访问第三方http请求

    这篇文章主要介绍了如何使用vue实现跨域访问第三方http请求,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2024-03-03
  • Vue入门之数据绑定(小结)

    Vue入门之数据绑定(小结)

    本篇文章主要介绍了探索Vue高阶组件的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01

最新评论