当前位置: 首页 > 后端技术 > Java

初识VUE响应式原理

时间:2023-04-01 23:05:58 Java

作者:京东零售吴京Vue自发布以来,就受到了开发者的青睐。提到Vue,我们首先想到的就是Vue的响应式系统。什么是响应系统?这是怎么回事?下面给大家简单介绍一下Vue中的响应式原理。vue2的响应式原理虽然Vue2在2023年12月31日就停止维护了,但是我们还是有很多项目是基于Vue2.X开发的,下面就简单的了解一下Vue2.X是基于什么的吧~Object.definePropertyVue2的响应式原理是基于对象的defineProperty()方法开发的,那么这个方法有什么作用呢?MDN是这样介绍的:**object.defineProperty()方法会直接在一个对象上定义一个新的属性,或者修改一个对象已有的属性,并返回这个对象。也就是说,我们可以通过对象的这个方法来精确的增加或修改对象的属性。每个对象都有一个获取/设置属性。当访问get属性时,会调用getter方法。当修改对象的属性值时,会调用setter方法。正式基于getter和setter方法,Vue可以使用Object.defineProperty来实现响应式系统。Vue中Object.defineProperty的使用在Vue中,当一个普通的JavaScript对象作为数据选项传入Vue实例时,Vue会遍历这个对象的所有属性,并使用object.defineProperty将这些属性转换成getter/setter,Getters/setters可以跟踪依赖关系并在访问属性时通知视图更改。Object.defineProperty(obj,'targetObj',{get(){//完成依赖收集},set(){//发生变化,同时通知相关依赖}})Vue3的响应式原理Vue2.0是很好地实现了数据的双向绑定,但是仍然存在一个非常重要的问题:由于Vue在初始化实例时会将属性转换为getter/setter,因此属性必须存在于数据对象上,Vue才能将其转换为响应格式数据。那么,如果要将新添加的需要特殊操作的对象或数组转换为响应式数据,则需要使用Vue.set等方法。Vue3很好的解决了这个问题。那么,Vue3是如何解决的呢?一起来看看吧~Proxy提到Vue3的数据拦截,首先要了解什么是proxy?Proxy可以理解为在目标对象之前设置了一层“拦截”。外部对对象的访问必须先经过这一层拦截。因此,提供了一种机制来过滤和重写外部访问。Proxy这个词的本意是代理,这里用它来表示它会“代理”某些操作,可以翻译为“代理”。原来Vue3使用的是Proxy代理,而不是Object.defineProperty方法。同样,proxy中也有get/set方法,例如~varobj=newProxy({},{get:function(target,name){returnname;},set:function(target,key,val){target[key]=val返回目标;}});通过为每个目标对象创建对应的Proxy对象,可以弥补Object.defineProperty不能监听新对象的缺陷。一个Vue3响应系统的简单设计实现一个简单响应系统的思路:?在读取(get)时,将副作用函数压入栈中;?设置(set)时,将副作用函数弹出栈,执行副作用函数。//Storesideeffectfunctionstackconstbucket=newSet()//StoreregisteredsideeffectfunctionletactiveEffect//注册sideeffectfunctionfunctioneffect(fn){//StoresideeffectfunctionactiveEffect=fnfn()}//副作用当函数fneffect(()=>{document.body.innerText=obj.text})执行匿名函数fn方法时,会触发响应式数据obj.text的读取操作,进而触发get拦截代理对象Proxy的函数:constProxy=newProxy(data,{get(target,key){if(activeEffect){bucket.add(activeEffect)}returntarget[key]},set(target,key,newVal){target[key]=newValbucket.forEach(fn=>fn())returntrue}})到这里,我们会发现有个问题,如何保证修改一个属性后触发副作用函数是我期望触发的副作用功能吗?为了解决这个问题,我们还需要在副作用函数和目标对象之间建立联系:我们只需要将Set数据结构替换为WeakMap:constbucket=newWeakMap()修改Proxy对象:constProxy=newProxy(data,{get(target,key){if(!activeEffect)returntarget[key]//先从栈中取出depsMap,depsMap存储的是目标对象和它的一对多关系相关副作用函数letdepsMap=bucket.get(target)if(!depsMap){bucket.set(target,(depsMap=newMap())}//根据key从depsMap中获取deps,deps保存所有与key关联的副作用函数letdeps=depsMap.get(key)if(!deps){depsMap.set(key,(deps=newSet())}deps.add(activeEffect)returntarget[key]},set(target,key,newVal){target[key]=newValconstdepsMap=bucket.get(target)如果(!depsMap)returnconsteffects=depsMap.get(key)effects&&effects.forEach(fn=>fn())}})这样我们就实现了一个简单的响应系统那么为什么要用weakMap而不是Map呢?留给大家自己思考吧~参考《Vue.js设计与实现_霍春阳》《ECMAScript 6入门》-阮一峰