关于Vue 监控数组的问题
作者:拜小白 发布时间:2024-05-28 15:52:13
常见面试题
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 这一层,在这里则可以监听数组以及各种数据类型的变化,无论是数组下标赋值引起变化还是数组方法引起变化,都可以被监听到,也可以避开监听数组每个属性下造成的性能问题。
参考
cn.vuejs.org/v2/guide/re…
www.jianshu.com/p/fc8da283c…
baijiahao.baidu.com/s?id=163912…
blog.csdn.net/XH_jing/art…
developer.mozilla.org/zh-CN/docs/…
来源:https://juejin.cn/post/7102368755275005959


猜你喜欢
- 前言官方手册:https://dev.mysql.com/doc/refman/5.7/en/server-logs.html不管是哪个数据
- mysql 中常常出现对中文支持不友好的情况常见的错误 “Illegal mix of collations for operation”下
- 由于pygame.movie.Movie.play() 只支持MPEG格式的视频,且 pygame版本大于1.9.5好像已经不支持这个模块了
- 通过神经网络实现线性回归的拟合训练过程只训练一轮的算法是:for 循环,直到所有样本数据使用完毕:读取一个样本数据前向计算反向传播更新梯度P
- 知识点:字符串在编程里,用的最多的就是字符串,字符串同时也是各类数据的中转站字符串运算符:编号运算符类型说明1+字符串拼接2*同一字符串多次
- 1 新建类库MyTestDLL2 右击项目“MyTestDLL”-》属性-》生成-》勾选“为COM互操作注册”3 打开 AssemblyIn
- 本文实例讲述了python通过floor函数舍弃小数位的方法。分享给大家供大家参考。具体分析如下:python中可以通过math库的floo
- 本文转自:https://blog.csdn.net/qq_42730750/article/details/108415551前言 各大
- # -*- coding: utf-8 -*- #mysqldb &nb
- 因工作需要,最近在学习使用python来解析各种文件,包括xmind,xml,excel,csv等等。在学习python解析XML的时候看到
- table估计每个跟web打过交道的人都会经常接触到,跟js结合能做出很多不错的体验。这里打算结合js做一个系列,包括一些操作和效果,虽然现
- 一、效果展示在介绍代码之前,先来看下本文的实现效果。可以参考下面步骤把Python文件转化成exe,发给未安装Python的他/她。Pins
- 本文实例讲述了Python带动态参数功能的sqlite工具类。分享给大家供大家参考,具体如下:最近在弄sqlite和python在网上参考各
- 作为抛砖引玉,用python3实现百度云语音解析,首先需要模拟Post请求把音频压缩文件丢给百度解析。但是遇到一个问题客户端怎麽丢数据都是返
- CSS是众所周知且应用广泛的网站样式语言,在它的版本三(CSS3)计划中,新增了一些能够节省时间的特性。尽管只有当前最新了浏览器
- 纯JS五子棋(各浏览器兼容)效果图: 代码下载HTML代码<!DOCTYPE html> <html> &
- 远程运行最怕断电,训练了几个小时的数据说没就没,或者停止运行。用nohup 记录代码的输出,还可以不受断电的影响。方法1. 用nohup 运
- 上下文管理器是一种 Python 构造,它提供了一个类似 try-finally 的环境,具有一致的接口和方便的语法,例如通过&ld
- 场景我们一般没必要过度优化 Go 程序性能。但是真正需要时,Go 提供的 pprof 工具能帮我们快速定位到问题。比如,我们团队之前有一个服
- 这篇文章主要介绍了JavaScript switch语句使用方法简介,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习