关于Vue 监控数组的问题

目录
  • 常见面试题
  • Vue 是如何追踪数据发生变化
  • Vue 如何更新数组
  • 为什么有些数组的数据变更不能被 Vue 监测到
  • Vue 为什么不能通过下标操作数组或者改变数组的长度来触发视图更新
  • Vue 3.0 是如何处理的?
  • 参考

常见面试题

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

(0)

相关推荐

  • 关于vue中如何监听数组变化

    前言 前段时间学习了关于vue中响应式数据的原理,(并作了学习笔记vue响应式原理),其实是通过Object.defineProperty控制getter和setter,并利用观察者模式完成的响应式设计.那么数组有一系列的操作方法,这些方法并不会触发数组的getter和setter方法.那么vue中针对数组的响应式设计是如何实现的呢...那么我们一起去学习下吧~ 源码部分 https://github.com/vuejs/vue/blob/dev/src/core/observer/array.

  • vuex state中的数组变化监听实例

    前言 首先,因为我有一个需求就是vue组件中有一组多选框,选中多选框的内容,要在另一个组件中进行视图更新,这个就设计的兄弟组件之间的通信了,兄弟组件之前通信我首先选用的vuex这个解决办法. 问题 vuex的state用来放数据,我就把数组放在了vuex中,然后设置了修改的函数.最终store.js中的代码如下: import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const state = { messArray:[] } c

  • Vue监听数组变化源码解析

    上一篇的代码中,忽略了对数组的处理,只关心了需要关心的部分,假装数组不存在. 这一篇开始考虑数组的问题. 从最简单的入手 先考虑一个问题,如何监听数组中的对象变化?忽略掉数组本身及其中的一般值,只考虑对象数组中的对象. 遍历数组,而后对数组中的每个对象调用 observe 方法 // 上一篇中出现的未曾重写的代码,这一篇中不再重复 var Observer = function Observer(value) { this.value = value; this.dep = new Dep();

  • vue中是怎样监听数组变化的

    我们知道通过Object.defineProperty()劫持数组为其设置getter和setter后,调用的数组的push.splice.pop等方法改变数组元素时并不会触发数组的setter,这就会造成使用上述方法改变数组后,页面上并不能及时体现这些变化,也就是数组数据变化不是响应式的(对上述不了解的可以参考这篇文章).但实际用vue开发时,对于响应式数组,使用push.splice.pop等方法改变数组时,页面会及时体现这种变化,那么vue中是如何实现的呢? 通过vue源码可以看出,vue

  • 关于Vue 监控数组的问题

    目录 常见面试题 Vue 是如何追踪数据发生变化 Vue 如何更新数组 为什么有些数组的数据变更不能被 Vue 监测到 Vue 为什么不能通过下标操作数组或者改变数组的长度来触发视图更新 Vue 3.0 是如何处理的? 参考 常见面试题 Vue 如何监控数组 defineProperty 真的不能监测数组变化吗? Vue 是如何追踪数据发生变化 在 Vue 中当我们把一个普通的 JS 对象作为 data 传入 Vue 实例,Vue2.x 对这个数据初始化时将遍历这个对象所有的属性,并使用 JS

  • vue 根据数组中某一项的值进行排序的方法

    vue中数组和对象的排序 1数组排序 <div id="app"> <ul> <li v-for="a in arr1">{{a}}</li> </ul> </div> <script type="text/javascript"> new Vue({ el:"#app", data:{ arr:[1,4,5,2,3,44] },compute

  • vue循环数组改变点击文字的颜色

    本文实例为大家分享了vue循环数组改变点击文字颜色的具体代码,供大家参考,具体内容如下 效果图 如下所示 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style type="text/css"> *{ list-style: none; margin: 0; padding: 0; } .

  • element vue Array数组和Map对象的添加与删除操作

    使用场景: 一个后台系统中, 管理员要配置自定义字段后台要生成id和title,其他角色要使用自定义字段的表单, 添加数据, 但是每个要填写的对象的id 和title都是无法固定的,因此页面显示的title 和id都需要自定义数字和map对象来实现,vue 的数值动态添加内容需要特定的方式: 1.定义一个vue空数组与一个vue 空Map对象: data: function() { return{ arrayData:[],//自定义字段中下拉框的数组 mapData:{},//自定义字段提交保

  • vue 解决数组赋值无法渲染在页面的问题

    在做一个网页重构成VUE的时候,有段代码是这样的 一直能打印出pics的值,但是就是无法渲染出来,检查了不是视图那边是错,最后发现其中有两处错误,一处是this指向问题,此地打印出来的this.pics并不是data里面的pics的值,后面把success函数改成箭头函数即可,还有vue中数组赋值数组不能直接赋值. 可以用push方法,下面是修改后的代码 以上这篇vue 解决数组赋值无法渲染在页面的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • vue更改数组中的值实例代码详解

    根据下标更改时 vm为新建的vue对象 ind为数组 第一个e为在数组ind中e索引位置 第二个e为更改为值e vm.$set(vm.ind,e,e) 常规更改 arr为数组 //添加 arr.push(1); //删除 arr.splice(*,*); //替换 arr.splice(*,*,*); splice方法 实例 例子 1 在本例中,我们将创建一个新数组,并向其添加一个元素: <script type="text/javascript"> var arr = n

  • vue 重塑数组之修改数组指定index的值操作

    如下所示: vm.items[indexOfItem] = newValue vue不能检测数组的变动 想要实现可以使用vue的set方法 this.$set(this.items,indexOfItem,newValue); 补充知识:vue中利用索引直接设置一个数组项,不能触发视图更新的问题 由于 JavaScript 的限制,Vue 不能检测以下数组的变动: 1.当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue 2.当你修改数组的长度

  • vue清空数组的几个方式(小结)

    目录 1. 前言 2. 清空数据的几种方式 2.1 使用ref() 2.2 使用slice 2.3 length赋值为0 2.4 使用splice 3. 总结 1. 前言 前两天在工作当中遇到一个问题,在vue3中使用reactive生成的响应式数组如何清空,当然我一般清空都是这么写: let array = [1,2,3]; array = []; 不过这么用在reactive代理的方式中还是有点问题,比如这样: let array = reactive([1,2,3]); watch(()=

  • vue给数组中对象排序 sort函数的用法

    目录 vue给数组中对象排序 sort函数 vue小技巧:简单排序和对象排序 对于数组里面全是number 对于一个对象 有多种类型 vue给数组中对象排序 sort函数 开发穿梭框的时候,需要将左侧选中的数据排序后添加到右侧 先看代码吧,后面解释 originalData为左侧选择的数据: var originalData =[{name: 'Tom'},{name: 'Andy'},{name: 'Marry'},{name: 'Tina'}]; 先给每个对象添加一个排序order: for

  • vue使用数组splice方法失效,且总删除最后一项的问题及解决

    目录 使用数组splice方法失效,总删除最后一项的问题 我的解决办法是 vue中splice()方法的使用 参数 使用方法 案例 使用数组splice方法失效,总删除最后一项的问题 今天在写项目的时候,遇到一个很简单的需求,下图,点击添加标签,左边出现一个可以输入的标签,点击删除按钮, 就能删除当前标签,很简单的需求,我却搞了一个多小时(哎…新手愚笨啊) 一看到这个我的思路就是点击添加标签,把新增的节点push到自己定义的数组里,然后渲染出来,点击删除按钮,用splice方法从数组中删除掉当前

随机推荐