2023年6月29日发(作者:)
vue监听数组元素属性的变化_为什么Vue3.0不再使⽤defineProperty实现数据监听?作者:我叫于是乎导 读vue3.0中,响应式数据部分弃⽤了Property,使⽤Proxy来代替它。本⽂将主要通过以下⽅⾯来分析为什么vue选择弃⽤Property。1. Property真的⽆法检测数组下标的变化吗?2. 分析vue2.x中对数组Observe部分源码3. 对⽐Property和Proxy⼀ ⽆法监控到数组下标的变化?在⼀些技术博客上看到过这样⼀种说法,认为 Property 有⼀个缺陷是⽆法监听数组变化:⽆法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。所以vue才设置了7个变异数组(push、pop、shift、unshift、splice、sort、reverse)的 hack ⽅法来解决问题。Property的第⼀个缺陷,⽆法监听数组变化。 然⽽Vue的⽂档提到了Vue是可以检测到数组变化的,但是只有以下⼋种⽅法,[indexOfItem] = newValue这种是⽆法检测的。这种说法是有问题的,事实上,Property 本⾝是可以监控到数组下标的变化的,只是在 Vue 的实现中,从性能/体验的性价⽐考虑,放弃了这个特性。下⾯我们通过⼀个例⼦来为 Property 正名:function defineReactive(data, key, value) { Property(data, key, { enumerable: true, configurable: true, get: function defineGet() { 上⾯代码对数组arr的每个属性通过 Property 进⾏劫持,下⾯我们对数组arr进⾏操作,看看哪些⾏为会触发数组的 getter和 setter ⽅法。1. 通过下标获取某个元素和修改某个元素的值可以看到,通过下标获取某个元素会触发 getter ⽅法, 设置某个值会触发 setter⽅法。接下来,我们再试⼀下数组的⼀些操作⽅法,看看是否会触发。2. 数组的 push ⽅法push 并未触发 setter 和 getter ⽅法,数组的下标可以看做是对象中的 key ,这⾥push 之后相当于增加了下索引为3的元素,但是并未对新的下标进⾏ observe ,所以不会触发。3.数组的 unshift ⽅法刚刚发⽣了什么?unshift 操作会导致原来索引为0,1,2,3的值发⽣变化,这就需要将原来索引为0,1,2,3的值取出来,然后重新赋值,所以取值的过程触发了 getter ,赋值时触发了 setter 。下⾯我们尝试通过索引获取⼀下对应的元素:只有索引为0,1,2的属性才会触发 getter 。这⾥我们可以对⽐对象来看,arr数组初始值为[1, 2, 3],即只对索引为0,1,2执⾏了 observe ⽅法,所以⽆论后来数组的长度发⽣怎样的变化,依然只有索引为0,1,2的元素发⽣变化才会触发,其他的新增索引,就相当于对象中新增的属性,需要再⼿动 observe 才可以。4. 数组的 pop ⽅法当移除的元素为引⽤为2的元素时,会触发 getter 。删除了索引为2的元素后,再去修改或获取它的值时,不会再触发 setter 和getter 。这和对象的处理是同样的,数组的索引被删除后,就相当于对象的属性被删除⼀样,不会再去触发 observe。到这⾥,我们可以简单的总结⼀下结论。Property 在数组中的表现和在对象中的表现是⼀致的,数组的索引就可以看做是对象中的 key。1. 通过索引访问或设置对应元素的值时,可以触发 getter 和 setter ⽅法2. 通过 push 或 unshift 会增加索引,对于新增加的属性,需要再⼿动初始化才能被observe。3. 通过 pop 或 shift 删除元素,会删除并更新索引,也会触发 setter 和 getter⽅法。所以,Property 是有监控数组下标变化的能⼒的,只是vue2.x放弃了这个特性。⼆ vue对数组的observe做了哪些处理?vue的 Observer 类定义在 core/observer/ 中可以看到,vue的 Observer 对数组做了单独的处理。hasProto 是判断数组的实例是否有 __proto__ 属性,如果有 __proto__ 属性就会执⾏ protoAugment ⽅法,将 arrayMethods 重写到原型上。 hasProto 定义如下。arrayMethods 是对数组的⽅法进⾏重写,定义在 core/observer/ 中, 下⾯是这部分源码的分析。/* * not type checking this file because flow doesn't play well with * dynamically accessing methods on Array prototype */import { def } from '../util/index'// 复制数组构三 Property VS Proxy上⾯已经知道 Property 对数组和对象的表现是⼀致的,那么它和Proxy 对⽐存在哪些优缺点呢?1. Property只能劫持对象的属性,⽽Proxy是直接代理对象。由于 Property 只能对属性进⾏劫持,需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。⽽ Proxy 直接代理对象,不需要遍历操作。2. Property对新增属性需要⼿动进⾏Observe。由于 Property 劫持的是对象的属性,所以新增属性时,需要重新遍历对象,对其新增属性再使⽤ Property进⾏劫持。也正是因为这个原因,使⽤vue给 data 中的数组或对象新增属性时,需要使⽤vm.$set 才能保证新增的属性也是相应式的。下⾯看⼀下vue的 set ⽅法是如何实现的,set⽅法定义在core/observer/ ,下⾯是核⼼代码。/** * Set a property on an object. Adds the new property and * triggers change notification if the property doesn't * already exist. */export function set (targe在 set ⽅法中,对 target 是数组和对象做了分别的处理,target 是数组时,会调⽤重写过的 splice ⽅法进⾏⼿动 Observe 。对于对象,如果 key 本来就是对象的属性,则直接修改值触发更新,否则调⽤defineReactive ⽅法重新定义响应式对象。如果采⽤ proxy 实现,Proxy 通过 set(target, propKey, value, receiver) 拦截对象属性的设置,是可以拦截到对象的新增属性的。不⽌如此,Proxy 对数组的⽅法也可以检测到,不需要像上⾯vue2.x源码中那样进⾏ hack。完美3. Proxy⽀持13种拦截操作,这是defineProperty所不具有的get(target, propKey, receiver):拦截对象属性的读取,⽐如和proxy['foo']。set(target, propKey, value, receiver):拦截对象属性的设置,⽐如 = v或proxy['foo'] = v,返回⼀个布尔值。has(target, propKey):拦截propKey in proxy的操作,返回⼀个布尔值。deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回⼀个布尔值。ownKeys(target):拦截PropertyNames(proxy)、PropertySymbols(proxy)、(proxy)、in循环,返回⼀个数组。该⽅法返回⽬标对象所有⾃⾝的属性的属性名,⽽()的返回结果仅包括⽬标对象⾃⾝的可遍历属性。getOwnPropertyDescriptor(target, propKey):拦截PropertyDescriptor(proxy, propKey),返回属性的描述对象。defineProperty(target, propKey, propDesc):拦截Property(proxy, propKey, propDesc)、Properties(proxy, propDescs),返回⼀个布尔值。preventExtensions(target):拦截tExtensions(proxy),返回⼀个布尔值。getPrototypeOf(target):拦截totypeOf(proxy),返回⼀个对象。isExtensible(target):拦截nsible(proxy),返回⼀个布尔值。setPrototypeOf(target, proto):拦截totypeOf(proxy, proto),返回⼀个布尔值。如果⽬标对象是函数,那么还有两种额外操作可以拦截。apply(target, object, args):拦截 Proxy 实例作为函数调⽤的操作,⽐如proxy(...args)、(object, ...args)、(...)。construct(target, args):拦截 Proxy 实例作为构造函数调⽤的操作,⽐如new proxy(...args)。4. 新标准性能红利Proxy 作为新标准,长远来看,JS引擎会继续优化 Proxy,但 getter 和 setter基本不会再有针对性优化。5. Proxy兼容性差可以看到,Proxy对于IE浏览器来说简直是灾难。四 总结1. Property 对数组和对象的表现⼀致,并⾮不能监控数组下标的变化,vue2.x中⽆法通过数组索引来实现响应式数据的⾃动更新是vue本⾝的设计导致的,不是 defineProperty 的锅。2. Property 和 Proxy 本质差别是,defineProperty 只能对属性进⾏劫持,所以出现了需要递归遍历,新增属性需要⼿动Observe 的问题。3. Proxy 作为新标准,浏览器⼚商势必会对其进⾏持续优化,但它的兼容性也是块硬伤,并且⽬前还没有完整的polyfill⽅案。推荐Vue学习资料⽂章:《「⼲货」学会这些Vue⼩技巧,可以早点下班和⼥神约会》《探索 Vue-Multiselect》《细品30张脑图带你从零开始学Vue》《Vue后台项⽬中遇到的技术难点以及解决⽅案》《⼿把⼿教你Electron + Vue实战教程(五)》《⼿把⼿教你Electron + Vue实战教程(四)》《⼿把⼿教你Electron + Vue实战教程(三)》《⼿把⼿教你Electron + Vue实战教程(⼆)》《⼿把⼿教你Electron + Vue实战教程(⼀)》《收集22种开源Vue模板和主题框架「⼲货」》《如何写出优秀后台管理系统?11个经典模版拿去不谢「⼲货」》《⼿把⼿教你实现⼀个Vue⾃定义指令懒加载》《基于 Vue 和⾼德地图实现地图组件「实践」》《⼀个由 Vue 作者尤⾬溪开发的 web 开发⼯具—vite》《是什么让我爱上了》《1.1万字深⼊细品Vue3.0源码响应式系统笔记「上」》《1.1万字深⼊细品Vue3.0源码响应式系统笔记「下」》《「实践」Vue 数据更新7 种情况汇总及延伸解决总结》《尤⼤⼤细说Vue3 的诞⽣之路「译」》《提⾼10倍打包速度⼯具Snowpack 2.0正式发布,再也不需要打包器》《⼤⼚Code Review总结Vue开发规范经验「值得学习」》《Vue3 插件开发详解尝鲜版「值得收藏」》《带你五步学会Vue SSR》《记⼀次Vue3.0技术⼲货分享会》《Vue 3.x 如何有惊⽆险地快速⼊门「进阶篇」》《「⼲货」微信⽀付前后端流程整理(Vue+Node)》《带你了解 vue-next(Vue 3.0)之 炉⽕纯青「实践」》《「⼲货」Vue+⾼德地图实现页⾯点击绘制多边形及多边形切割拆分》《「⼲货」Vue+Element前端导⼊导出Excel》《「实践」Deno bytes 模块全解析》《细品实践解决含⽔印、电⼦签章问题「Vue篇」》《基于vue + element的后台管理系统解决⽅案》《Vue仿蘑菇街商城项⽬(vue+koa+mongodb)》《基于 electron-vue 开发的⾳乐播放器「实践」》《「实践」Vue项⽬中标配编辑器插件Vue-Quill-Editor》《基于 Vue 技术栈的微前端⽅案实践》《消息队列助你成为⾼薪 ⼯程师》《 中的 stream 模块详解》《「⼲货」Deno TCP Echo Server 是怎么运⾏的?》《「⼲货」了不起的 Deno 实战教程》《「⼲货」通俗易懂的Deno ⼊门教程》《Deno 正式发布,彻底弄明⽩和 node 的区别》《「实践」基于Apify+node+react/vue搭建⼀个有点意思的爬⾍平台》《「实践」深⼊对⽐ Vue 3.0 Composition API 和 React Hooks》《前端⽹红框架的插件机制全梳理(axios、koa、redux、vuex)》《深⼊Vue 必学⾼阶组件 HOC「进阶篇」》《深⼊学习Vue的data、computed、watch来实现最精简响应式系统》《10个实例⼩练习,快速⼊门熟练 Vue3 核⼼新特性(⼀)》《10个实例⼩练习,快速⼊门熟练 Vue3 核⼼新特性(⼆)》《教你部署搭建⼀个Vue-cli4+Webpack移动端框架「实践」》《2020前端就业Vue框架篇「实践」》《详解Vue3中 router 带来了哪些变化?》《Vue项⽬部署及性能优化指导篇「实践」》《Vue⾼性能渲染⼤数据Tree组件「实践」》《尤⼤⼤细品VuePress搭建技术⽹站与个⼈博客「实践」》《10个Vue开发技巧「实践」》《是什么导致尤⼤⼤选择放弃Webpack?【vite 原理解析】》《带你了解 vue-next(Vue 3.0)之 ⼩试⽜⼑【实践】》《带你了解 vue-next(Vue 3.0)之 初⼊茅庐【实践】》《实践Vue 3.0做JSX(TSX)风格的组件开发》《⼀篇⽂章教你并列⽐较和的语法【实践】》《⼿拉⼿带你开启Vue3世界的⿁斧神⼯【实践】》《深⼊浅出通过vue-cli3构建⼀个SSR应⽤程序【实践】》《怎样为你的 单页应⽤提速》《聊聊昨晚尤⾬溪现场针对Vue3.0 Beta版本新特性知识点汇总》《【新消息】Vue 3.0 Beta 版本发布,你还学的动么?》《Vue真是太好了 壹万多字的Vue知识点 超详细!》《Vue + Koa从零打造⼀个H5页⾯可视化编辑器——Quark-h5》《深⼊浅出Vue3 跟着尤⾬溪学 TypeScript 之 Ref 【实践】》《⼿把⼿教你深⼊浅出vue-cli3升级vue-cli4的⽅法》《Vue 3.0 Beta 和React 开发者分别杠上了》《⼿把⼿教你⽤vue drag chart 实现⼀个可以拖动 / 缩放的图表组件》《Vue3 尝鲜》《总结Vue组件的通信》《Vue 开源项⽬ TOP45》《2020 年,Vue 受欢迎程度是否会超过 React?》《尤⾬溪:Vue 3.0的设计原则》《使⽤vue实现HTML页⾯⽣成图⽚》《实现全栈收银系统(Node+Vue)(上)》《实现全栈收银系统(Node+Vue)(下)》《vue引⼊原⽣⾼德地图》《Vue合理配置WebSocket并实现群聊》《多年vue项⽬实战经验汇总》《vue之将echart封装为组件》《基于 Vue 的两层吸顶踩坑总结》《Vue插件总结【前端开发必备】》《Vue 开发必须知道的 36 个技巧【近1W字】》《构建⼤型 项⽬的10条建议》《深⼊理解vue中的slot与slot-scope》《⼿把⼿教你Vue解析pdf(base64)转图⽚【实践】》《使⽤vue+node搭建前端异常监控系统》《推荐 8 个漂亮的 进度条组件》《基于Vue实现拖拽升级(九宫格拖拽)》《⼿摸⼿,带你⽤vue撸后台 系列⼆(登录权限篇)》《⼿摸⼿,带你⽤vue撸后台 系列三(实战篇)》《前端框架⽤vue还是react?清晰对⽐两者差异》《Vue组件间通信⼏种⽅式,你⽤哪种?【实践】》《浅析 React / Vue 跨端渲染原理与实现》《10个Vue开发技巧助⼒成为更好的⼯程师》《⼿把⼿教你Vue之⽗⼦组件间通信实践讲解【props、$ref 、$emit】》《1W字长⽂+多图,带你了解vue的双向数据绑定源码实现》《深⼊浅出Vue3 的响应式和以前的区别到底在哪⾥?【实践】》《⼲货满满!如何优雅简洁地实现时钟翻牌器(⽀持JS/Vue/React)》《基于Vue/VueRouter/Vuex/Axios登录路由和接⼝级拦截原理与实现》《⼿把⼿教你 实现数据可视化极速上⼿到Vue应⽤》《吃透 Vue 项⽬开发实践|16个⽅⾯深⼊前端⼯程化开发技巧【上】》《吃透 Vue 项⽬开发实践|16个⽅⾯深⼊前端⼯程化开发技巧【中】》《吃透 Vue 项⽬开发实践|16个⽅⾯深⼊前端⼯程化开发技巧【下】》《Vue3.0权限管理实现流程【实践】》《后台管理系统,前端Vue根据⾓⾊动态设置菜单栏和路由》作者:我叫于是乎
发布者:admin,转转请注明出处:http://www.yc00.com/web/1687986024a63980.html
评论列表(0条)