很多朋友对于网站源码分享hackv和网站源码分享网免费不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!
作者:我叫于是乎
转发链接:https://mp.weixin.qq.com/s/ck4e_YqvAx-MDadSodBKcQ
导读
vue3.0中,响应式数据部分弃用了Object.defineProperty,使用Proxy来代替它。本文将主要通过以下方面来分析为什么vue选择弃用Object.defineProperty。
1.Object.defineProperty真的无法检测数组下标的变化吗?
2.分析vue2.x中对数组Observe部分源码
3.对比Object.defineProperty和Proxy
一无法监控到数组下标的变化?
在一些技术博客上看到过这样一种说法,认为Object.defineProperty有一个缺陷是无法监听数组变化:
无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。所以vue才设置了7个变异数组(push、pop、shift、unshift、splice、sort、reverse)的hack方法来解决问题。
Object.defineProperty的第一个缺陷,无法监听数组变化。然而Vue的文档提到了Vue是可以检测到数组变化的,但是只有以下八种方法,vm.items[indexOfItem]=newValue这种是无法检测的。
这种说法是有问题的,事实上,Object.defineProperty本身是可以监控到数组下标的变化的,只是在Vue的实现中,从性能/体验的性价比考虑,放弃了这个特性。
下面我们通过一个例子来为Object.defineProperty正名:
functiondefineReactive(data,key,value){\nObject.defineProperty(data,key,{\nenumerable:true,\nconfigurable:true,\nget:functiondefineGet(){\nconsole.log(`getkey:${key}value:${value}`)\nreturnvalue\n},\nset:functiondefineSet(newVal){\nconsole.log(`setkey:${key}value:${newVal}`)\nvalue=newVal\n}\n})\n}\n\nfunctionobserve(data){\nObject.keys(data).forEach(function(key){\ndefineReactive(data,key,data[key])\n})\n}\n\nletarr=[1,2,3]\nobserve(arr)
上面代码对数组arr的每个属性通过Object.defineProperty进行劫持,下面我们对数组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。
到这里,我们可以简单的总结一下结论。
Object.defineProperty在数组中的表现和在对象中的表现是一致的,数组的索引就可以看做是对象中的key。
1.通过索引访问或设置对应元素的值时,可以触发getter和setter方法
2.通过push或unshift会增加索引,对于新增加的属性,需要再手动初始化才能被observe。
3.通过pop或shift删除元素,会删除并更新索引,也会触发setter和getter方法。
所以,Object.defineProperty是有监控数组下标变化的能力的,只是vue2.x放弃了这个特性。
二vue对数组的observe做了哪些处理?
vue的Observer类定义在core/observer/index.js中
可以看到,vue的Observer对数组做了单独的处理。
hasProto是判断数组的实例是否有__proto__属性,如果有__proto__属性就会执行protoAugment方法,将arrayMethods重写到原型上。hasProto定义如下。
arrayMethods是对数组的方法进行重写,定义在core/observer/array.js中,下面是这部分源码的分析。
/*\n*nottypecheckingthisfilebecauseflowdoesn&39;../util/index&39;push&39;pop&39;shift&39;unshift&39;splice&39;sort&39;reverse&39;push&39;unshift&39;splice&39;t\n*alreadyexist.\n*/\nexportfunctionset(target:Array<any>|Object,key:any,val:any):any{\n//如果target是数组,且key是有效的数组索引,会调用数组的splice方法,\n//我们上面说过,数组的splice方法会被重写,重写的方法中会手动Observe\n//所以vue的set方法,对于数组,就是直接调用重写splice方法\nif(Array.isArray(target)&&isValidArrayIndex(key)){\ntarget.length=Math.max(target.length,key)\ntarget.splice(key,1,val)\nreturnval\n}\n//对于对象,如果key本来就是对象中的属性,直接修改值就可以触发更新\nif(keyintarget&&!(keyinObject.prototype)){\ntarget[key]=val\nreturnval\n}\n//vue的响应式对象中都会添加了__ob__属性,所以可以根据是否有__ob__属性判断是否为响应式对象\nconstob=(target:any).__ob__\n//如果不是响应式对象,直接赋值\nif(!ob){\ntarget[key]=val\nreturnval\n}\n//调用defineReactive给数据添加了getter和setter,\n//所以vue的set方法,对于响应式的对象,就会调用defineReactive重新定义响应式对象,defineReactive函数\ndefineReactive(ob.value,key,val)\nob.dep.notify()\nreturnval\n}
在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和proxy[&39;]。set(target,propKey,value,receiver):拦截对象属性的设置,比如proxy.foo=v或proxy[&39;]=v,返回一个布尔值。has(target,propKey):拦截propKeyinproxy的操作,返回一个布尔值。deleteProperty(target,propKey):拦截deleteproxy[propKey]的操作,返回一个布尔值。ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。getOwnPropertyDescriptor(target,propKey):拦截Object.getOwnPropertyDescriptor(proxy,propKey),返回属性的描述对象。defineProperty(target,propKey,propDesc):拦截Object.defineProperty(proxy,propKey,propDesc)、Object.defineProperties(proxy,propDescs),返回一个布尔值。preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。setPrototypeOf(target,proto):拦截Object.setPrototypeOf(proxy,proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。apply(target,object,args):拦截Proxy实例作为函数调用的操作,比如proxy(…args)、proxy.call(object,…args)、proxy.apply(…)。construct(target,args):拦截Proxy实例作为构造函数调用的操作,比如newproxy(…args)。
4.新标准性能红利
Proxy作为新标准,长远来看,JS引擎会继续优化Proxy,但getter和setter基本不会再有针对性优化。
5.Proxy兼容性差
可以看到,Proxy对于IE浏览器来说简直是灾难。
并且目前并没有一个完整支持Proxy所有拦截方法的Polyfill方案,有一个google编写的proxy-polyfill(https://github.com/GoogleChrome/proxy-polyfill)也只支持了get,set,apply,construct四种拦截,可以支持到IE9+和Safari6+。
四总结
1.Object.defineProperty对数组和对象的表现一致,并非不能监控数组下标的变化,vue2.x中无法通过数组索引来实现响应式数据的自动更新是vue本身的设计导致的,不是defineProperty的锅。
2.Object.defineProperty和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》
《是什么让我爱上了Vue.js》
《1.1万字深入细品Vue3.0源码响应式系统笔记「上」》
《1.1万字深入细品Vue3.0源码响应式系统笔记「下」》
《「实践」Vue数据更新7种情况汇总及延伸解决总结》
《尤大大细说Vue3的诞生之路「译」》
《提高10倍打包速度工具Snowpack2.0正式发布,再也不需要打包器》
《大厂CodeReview总结Vue开发规范经验「值得学习」》
《Vue3插件开发详解尝鲜版「值得收藏」》
《带你五步学会VueSSR》
《记一次Vue3.0技术干货分享会》
《Vue3.x如何有惊无险地快速入门「进阶篇」》
《「干货」微信支付前后端流程整理(Vue+Node)》
《带你了解vue-next(Vue3.0)之炉火纯青「实践」》
《「干货」Vue+高德地图实现页面点击绘制多边形及多边形切割拆分》
《「干货」Vue+Element前端导入导出Excel》
《「实践」Denobytes模块全解析》
《细品pdf.js实践解决含水印、电子签章问题「Vue篇」》
《基于vue+element的后台管理系统解决方案》
《Vue仿蘑菇街商城项目(vue+koa+mongodb)》
《基于electron-vue开发的音乐播放器「实践」》
《「实践」Vue项目中标配编辑器插件Vue-Quill-Editor》
《基于Vue技术栈的微前端方案实践》
《消息队列助你成为高薪Node.js工程师》
《Node.js中的stream模块详解》
《「干货」DenoTCPEchoServer是怎么运行的?》
《「干货」了不起的Deno实战教程》
《「干货」通俗易懂的Deno入门教程》
《Deno正式发布,彻底弄明白和node的区别》
《「实践」基于Apify+node+react/vue搭建一个有点意思的爬虫平台》
《「实践」深入对比Vue3.0CompositionAPI和ReactHooks》
《前端网红框架的插件机制全梳理(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(Vue3.0)之小试牛刀【实践】》
《带你了解vue-next(Vue3.0)之初入茅庐【实践】》
《实践Vue3.0做JSX(TSX)风格的组件开发》
《一篇文章教你并列比较React.js和Vue.js的语法【实践】》
《手拉手带你开启Vue3世界的鬼斧神工【实践】》
《深入浅出通过vue-cli3构建一个SSR应用程序【实践】》
《怎样为你的Vue.js单页应用提速》
《聊聊昨晚尤雨溪现场针对Vue3.0Beta版本新特性知识点汇总》
《【新消息】Vue3.0Beta版本发布,你还学的动么?》
《Vue真是太好了壹万多字的Vue知识点超详细!》
《Vue+Koa从零打造一个H5页面可视化编辑器——Quark-h5》
《深入浅出Vue3跟着尤雨溪学TypeScript之Ref【实践】》
《手把手教你深入浅出vue-cli3升级vue-cli4的方法》
《Vue3.0Beta和React开发者分别杠上了》
《手把手教你用vuedragchart实现一个可以拖动/缩放的图表组件》
《Vue3尝鲜》
《总结Vue组件的通信》
《Vue开源项目TOP45》
《2020年,Vue受欢迎程度是否会超过React?》
《尤雨溪:Vue3.0的设计原则》
《使用vue实现HTML页面生成图片》
《实现全栈收银系统(Node+Vue)(上)》
《实现全栈收银系统(Node+Vue)(下)》
《vue引入原生高德地图》
《Vue合理配置WebSocket并实现群聊》
《多年vue项目实战经验汇总》
《vue之将echart封装为组件》
《基于Vue的两层吸顶踩坑总结》
《Vue插件总结【前端开发必备】》
《Vue开发必须知道的36个技巧【近1W字】》
《构建大型Vue.js项目的10条建议》
《深入理解vue中的slot与slot-scope》
《手把手教你Vue解析pdf(base64)转图片【实践】》
《使用vue+node搭建前端异常监控系统》
《推荐8个漂亮的vue.js进度条组件》
《基于Vue实现拖拽升级(九宫格拖拽)》
《手摸手,带你用vue撸后台系列二(登录权限篇)》
《手摸手,带你用vue撸后台系列三(实战篇)》
《前端框架用vue还是react?清晰对比两者差异》
《Vue组件间通信几种方式,你用哪种?【实践】》
《浅析React/Vue跨端渲染原理与实现》
《10个Vue开发技巧助力成为更好的工程师》
《手把手教你Vue之父子组件间通信实践讲解【props、$ref、$emit】》
《1W字长文+多图,带你了解vue的双向数据绑定源码实现》
《深入浅出Vue3的响应式和以前的区别到底在哪里?【实践】》
《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》
《基于Vue/VueRouter/Vuex/Axios登录路由和接口级拦截原理与实现》
《手把手教你D3.js实现数据可视化极速上手到Vue应用》
《吃透Vue项目开发实践|16个方面深入前端工程化开发技巧【上】》
《吃透Vue项目开发实践|16个方面深入前端工程化开发技巧【中】》
《吃透Vue项目开发实践|16个方面深入前端工程化开发技巧【下】》
《Vue3.0权限管理实现流程【实践】》
《后台管理系统,前端Vue根据角色动态设置菜单栏和路由》
作者:我叫于是乎
转发链接:https://mp.weixin.qq.com/s/ck4e_YqvAx-MDadSodBKcQ
如果你还想了解更多这方面的信息,记得收藏关注本站。
