浅谈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 相关文章推荐
innerHTML中标签可以换行的方法汇总
Aug 14 Javascript
基于JavaScript实现表单密码的隐藏和显示出来
Mar 02 Javascript
Angular的模块化(代码分享)
Dec 26 Javascript
jQuery实现的简单悬浮层功能完整实例
Jan 23 Javascript
为什么使用koa2搭建微信第三方公众平台的原因
May 16 Javascript
微信小程序scroll-x失效的完美解决方法
Jul 18 Javascript
原生JS实现的轮播图功能详解
Aug 06 Javascript
node中IO以及定时器优先级详解
May 10 Javascript
小程序云开发教程如何使用云函数实现点赞功能
May 18 Javascript
vue调用语音播放的方法
Sep 27 Javascript
JavaScript Canvas编写炫彩的网页时钟
Oct 16 Javascript
vue结合el-upload实现腾讯云视频上传功能
Jul 01 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
隐藏X-Space个人空间下方版权方法隐藏X-Space个人空间标题隐藏X-Space个人空间管理版权方法
2007/02/22 PHP
非常不错的MySQL优化的8条经验
2008/03/24 PHP
PHP递归算法的详细示例分析
2013/02/19 PHP
Cygwin中安装PHP方法步骤
2015/07/04 PHP
dvwa+xampp搭建显示乱码的问题及解决方案
2015/08/23 PHP
PHP快速排序quicksort实例详解
2016/09/28 PHP
php_pdo 预处理语句详解
2016/11/21 PHP
laravel 实现关闭CSRF(全部关闭、部分关闭)
2019/10/21 PHP
JS自动适应的图片弹窗实例
2013/06/29 Javascript
Jquery跨域获得Json的简单实例
2016/05/18 Javascript
jQuery.Callbacks()回调函数队列用法详解
2016/06/14 Javascript
微信小程序 教程之WXSS
2016/10/18 Javascript
Angular.JS通过指令操作DOM的方法
2017/05/10 Javascript
laydate 显示结束时间不小于开始时间的实例
2017/08/11 Javascript
微信小程序使用checkbox显示多项选择框功能【附源码下载】
2017/12/11 Javascript
浅谈关于iview表单验证的问题
2018/09/29 Javascript
详解如何在Node.js的httpServer中接收前端发送的arraybuffer数据
2018/11/11 Javascript
谈谈JavaScript中super(props)的重要性
2019/02/12 Javascript
关于layui的下拉搜索框异步加载数据的解决方法
2019/09/28 Javascript
vue+echarts实现动态折线图的方法与注意
2020/09/01 Javascript
[01:04:22]2018DOTA2亚洲邀请赛 3.31 小组赛 B组 IG vs EG
2018/04/01 DOTA
python文件操作整理汇总
2014/10/21 Python
Python的math模块中的常用数学函数整理
2016/02/04 Python
浅谈Python使用Bottle来提供一个简单的web服务
2017/12/27 Python
Python实现改变与矩形橡胶的线条的颜色代码示例
2018/01/05 Python
Python爬取365好书中小说代码实例
2020/02/28 Python
CSS3弹性盒模型开发笔记(二)
2016/04/26 HTML / CSS
关于HTML5的22个初级技巧(图文教程)
2012/06/21 HTML / CSS
Tory Burch美国官方网站:美国时尚生活品牌
2016/08/01 全球购物
Steiff台湾官网:德国金耳釦泰迪熊
2019/12/26 全球购物
最新教师自我评价分享
2013/11/12 职场文书
抵押贷款承诺书
2014/05/30 职场文书
领导班子四风问题个人对照检查材料
2014/10/04 职场文书
后勤工作个人总结
2015/02/28 职场文书
2015年三万活动总结
2015/03/25 职场文书
青年志愿者服务活动总结
2015/05/06 职场文书