浅谈Vue为什么不能检测数组变动


Posted in Javascript onOctober 14, 2019

问题来源:https://segmentfault.com/q/1010000015780995

问题描述:Vue检测数据的变动是通过Object.defineProperty实现的,所以无法监听数组的添加操作是可以理解的,因为是在构造函数中就已经为所有属性做了这个检测绑定操作。

但是官方的原文:由于 JavaScript 的限制, Vue 不能检测以下变动的数组:

当你利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如: vm.items.length = newLength

这句话是什么意思?我测试了下Object.defineProperty是可以通过索引属性来设置属性的访问器属性的,那为何做不了监听?

有些论坛上的人说因为数组长度是可变的,即使长度为5,但是未必有索引4,我就想问问这个答案哪里来的,修改length,新增的元素会被添加到最后,它的值为undefined,通过索引一样可以获取他们的值,怎么就叫做“未必有索引4”了呢?

既然知道数组的长度为何不能遍历所有元素并通过索引这个属性全部添加set和get不就可以同时更新视图了吗?

如果非要说的话,考虑到性能的问题,假设元素内容只有4个有意义的值,但是长度确实1000,我们不可能为1000个元素做检测操作。但是官方说的由于JS限制,我想知道这个限制是什么内容?各位大大帮我解决下这个问题,感谢万分

面对这个问题,我想说的是,首先,长度为1000,但只有4个元素的数组并不一定会影响性能,因为js中对数据的遍历除了for循环还有forEach、map、filter、some等,除了for循环外(for,for...of),其他的遍历都是对键值的遍历,也就是除了那四个元素外的空位并不会进行遍历(执行回调),所以也就不会造成性能损耗,因为循环体中没有操作的话,所带来的性能影响可以忽略不计,下面是长度为10000,但只有两个元素的数组分别使用for及forEach遍历的结果:

var arr = [1]; arr[10000] = 1
function a(){
  console.time()
  for(var i = 0;i<arr.length;i++)console.log(1)
  console.timeEnd()
}
a(); //default: 567.1669921875ms
a(); //default: 566.2451171875ms

function b(){
  console.time()
  arr.forEach(item=>{console.log(2)})
  console.timeEnd()
}
b(); //default: 0.81982421875ms
b(); //default: 0.434814453125ms

可以看到结果非常明显,不过,如果for循环中不做操作的话两者速度差不多

其次,我要说的是,我也不知道这个限制是什么 (⇀‸↼‶) ?( •́ω•̀ )?

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。数组的索引也是属性,所以我们是可以监听到数组元素的变化的

var arr = [1,2,3,4]
arr.forEach((item,index)=>{
  Object.defineProperty(arr,index,{
    set:function(val){
      console.log('set')
      item = val
    },
    get:function(val){
      console.log('get')
      return item
    }
  })
})
arr[1]; // get 2
arr[1] = 1; // set 1

但是我们新增一个元素,就不会触发监听事件,因为这个新属性我们并没有监听,删除一个属性也是。

再回到题主的问题,既然数组是可以被监听的,那为什么vue不能检测vm.items[indexOfItem] = newValue导致的数组元素改变呢,哪怕这个下标所对应的元素是存在的,且被监听了的?

为了搞清楚这个问题,我用vue的源码测试了下,下面是vue对数据监测的源码:

浅谈Vue为什么不能检测数组变动

可以看到,当数据是数组时,会停止对数据属性的监测,我们修改一下源码:

浅谈Vue为什么不能检测数组变动

使数据为数组时,依然监测其属性,然后在defineReactive函数中的get,set打印一些东西,方便我们知道调用了get以及set。这里加了个简单判断,只看数组元素的get,set

浅谈Vue为什么不能检测数组变动

然后写了一个简单案例,主要测试使用vm.items[indexOfItem] = newValue改变数组元素能不能被监测到,并响应式的渲染页面

浅谈Vue为什么不能检测数组变动

运行页面

浅谈Vue为什么不能检测数组变动

可以看到,运行了6次get,我们数组长度为3,也就是说数组被遍历了两遍。两遍不多,页面渲染一次,可能多次触发一个数据的监听事件,哪怕这个数据只用了一次,具体的需要看尤大代码怎么写的。就拿这个来说,当监听的数据为数组时,会运行dependArray函数(代码在上面图中get的实现里),这个函数里对数组进行了遍历取值操作,所以会多3遍get,这里主要是vue对data中arr数组的监听触发了dependArray函数。

当我们点击其中一个元素的时候,比如我点击的是3

浅谈Vue为什么不能检测数组变动

可以看到会先运行一次set,然后数据更新,重新渲染页面,数组又是被遍历了两遍。

但是!!!数组确实变成响应式的了,也就是说js语法功能并不会限制数组的监测。

这里我们是用长度为3的数组测试的,当我把数组长度增加到9时

浅谈Vue为什么不能检测数组变动

可以看到,运行了18次get,数组还是被遍历了两遍,点击某个元素同理,渲染的时候也是被遍历两次。

浅谈Vue为什么不能检测数组变动

有了上面的实验,我的结论是数组在vue中是可以实现响应式更新的,但是不明白尤大是出于什么考虑,没有加入这一功能,希望有知道的大佬们不吝赐教

2018-07-27补充

github上提问了尤大

浅谈Vue为什么不能检测数组变动

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
关于js中window.location.href,location.href,parent.location.href,top.location.href的用法与区别
Oct 18 Javascript
jquery ajax post提交数据乱码
Nov 05 Javascript
从数组中随机取x条不重复数据的JS代码
Dec 24 Javascript
JS cookie中文乱码解决方法
Jan 28 Javascript
在jQuery中处理XML数据的大致方法
Aug 14 Javascript
js实现简单的验证码
Dec 25 Javascript
基于jQuery实现点击最后一行实现行自增效果的表格
Jan 12 Javascript
Bootstrap进度条实现代码解析
Mar 07 Javascript
微信小程序中post方法与get方法的封装
Sep 26 Javascript
Vue不能观察到数组length的变化
Jun 08 Javascript
easyUI使用分页过滤器对数据进行分页操作实例分析
Jun 01 Javascript
原生js实现贪吃蛇游戏
Oct 26 Javascript
为什么Vue3.0使用Proxy实现数据监听(defineProperty表示不背这个锅)
Oct 14 #Javascript
Vue3.0中的monorepo管理模式的实现
Oct 14 #Javascript
Vue3 源码导读(推荐)
Oct 14 #Javascript
基于JS实现父组件的请求服务过程解析
Oct 14 #Javascript
JavaScript this在函数中的指向及实例详解
Oct 14 #Javascript
vue循环数组改变点击文字的颜色
Oct 14 #Javascript
基于纯JS实现多张图片的懒加载Lazy过程解析
Oct 14 #Javascript
You might like
一个自定义位数的php多用户计数器代码
2007/03/11 PHP
yii框架配置默认controller和action示例
2014/04/30 PHP
php实现获取局域网所有用户的电脑IP和主机名、及mac地址完整实例
2014/07/18 PHP
PHP命名空间(namespace)的使用基础及示例
2014/08/18 PHP
php微信支付之APP支付方法
2015/03/04 PHP
php通过asort()给关联数组按照值排序的方法
2015/03/18 PHP
php使用Image Magick将PDF文件转换为JPG文件的方法
2015/04/01 PHP
详解WordPress中过滤链接与过滤SQL语句的方法
2015/12/18 PHP
浅谈使用 Yii2 AssetBundle 中 $publishOptions 的正确姿势
2017/11/08 PHP
Yii框架实现对数据库的CURD操作示例
2019/09/03 PHP
jquery 动态创建元素的方式介绍及应用
2013/04/21 Javascript
Jquery 实现表格颜色交替变化鼠标移过颜色变化实例
2013/08/28 Javascript
js实现获取焦点后光标在字符串后
2014/09/17 Javascript
JQuery中clone方法复制节点
2015/05/18 Javascript
JavaScript表单焦点自动切换代码
2016/07/24 Javascript
Bootstrap modal 多弹窗之叠加关闭阴影遮罩问题的解决方法
2017/02/27 Javascript
微信小程序 开发之全局配置
2017/05/05 Javascript
浅谈原生JS中的延迟脚本和异步脚本
2017/07/12 Javascript
Vuejs在v-for中,利用index来对第一项添加class的方法
2018/03/03 Javascript
vue对storejs获取的数据进行处理时遇到的几种问题小结
2018/03/20 Javascript
JavaScript数据结构与算法之二叉树实现查找最小值、最大值、给定值算法示例
2019/03/01 Javascript
python实现无证书加密解密实例
2014/10/27 Python
简化Python的Django框架代码的一些示例
2015/04/20 Python
Python解决两个整数相除只得到整数部分的实例
2018/11/10 Python
keras K.function获取某层的输出操作
2020/06/29 Python
python更新数据库中某个字段的数据(方法详解)
2020/11/18 Python
python 基于opencv去除图片阴影
2021/01/26 Python
AE美国鹰日本官方网站: American Eagle Outfitters
2016/12/10 全球购物
构造方法和其他方法的区别?怎么调用父类的构造方法
2013/09/22 面试题
九州传奇上机题
2014/07/10 面试题
幼儿园中班下学期评语
2014/04/18 职场文书
学雷锋演讲稿汇总
2014/05/10 职场文书
超市开店计划书
2014/09/15 职场文书
公务员个人年终总结
2015/02/12 职场文书
黑暗中的舞者观后感
2015/06/18 职场文书
教你如何使用Python下载B站视频的详细教程
2021/04/29 Python