当前位置: 首页 > 科技观察

Vue3解构赋值失去响应式思维!_0

时间:2023-03-15 16:03:34 科技观察

前言Vue3自发布两年来风头正劲,如今大有与React共享天下的气势。我们知道它是基于proxy实现响应式的能力,解决了vue2遗留下来的一些问题,同时由于proxy的特性,也提升了runtime的性能。任何事物都有优点和缺点。proxy虽然无敌,但是也有它的局限性,导致了我认为的一些弊端(其实不符合js语言的自然写法,有人认为是一种特殊的写法,就是不是缺点):原始值的响应式系统的实现导致将其包装为对象并通过.value访问它;ES6解构,不能随意使用。会破坏他的反应能力。在好奇心的驱使下,他研究思考自己为什么会造成这两种不利。原值响应系统的实现在了解原值响应系统的实现之前,我们先来回顾一下代理的能力!constobj={name:'win'}consthandler={get:function(target,key){console.log('get--',key)returnReflect.get(...arguments)},set:函数(target,key,value){console.log('set--',key,'=',value)returnReflect.set(...arguments)}}constdata=newProxy(obj,handler)data.name='ten'console.log(data.name,'data.name22')在上面的代码中,我们发现使用proxy本身就是拦截对象,通过newProxy的返回值拦截obj对象。这样,当你访问对象中的值时,会触发get方法,当你修改对象中的值时,会触发set方法。但是涉及到原来的值,他就没有对象了,那怎么办,新的代理也没用。无奈之下,我们只能把它包裹起来,只好使用.value访问。让我们看一下实现:import{reactive}from"./reactive";import{trackEffects,triggerEffects}from'./effect'exportconstisObject=(value)=>{returntypeofvalue==='object'&&value!==null}//将对象转换为反应函数toReactive(value){返回isObject(值)?反应性(价值):价值}类RefImpl{公共_价值;公共部门=新集;//依赖集合public__v_isRef=true;//是ref的标识//rawValue构造函数传入的值(publicrawValue,public_shallow){//1.如果是对象,使用reactive将对象转换为responsive//Shallowref不再需要Proxythis._value=_shallow了吗?原始值:toReactive(原始值);}getvalue(){//依赖集合trackEffects(this.dep)returnthis._value;}setvalue(newVal){if(newVal!==this.rawValue){//2.如果set的值不等于初始值,判断新值是否为对象,赋值this._value=这._浅?newVal:toReactive(newVal);//赋值后改变初始值Forthis.rawValue=newValtri这次ggerEffects(this.dep)}}}上面的代码是对原值的包装。被包装成一个对象,通过getvalue和setvalue方法获取原始值,导致必须进行.value操作,这其实是一个无奈的选择,相当于两瓶毒药,你一定要选一瓶鱼和熊掌。为什么ES6解构不能随意使用,会破坏它的响应性。第一个问题终于清楚了,那么我们来看看最重要的第二个问题,为什么结构赋值会破坏响应性。Proxy背景在我们开始之前,让我们讨论一下为什么我们需要更改响应式方案。Vue2基于Object.defineProperty,但是有很多缺陷,比如不能监听基于下标的数组修改,不支持Map、Set、WeakMap、WeakSet等缺陷。其实这些并不耽误我们的发展。直到现在,Vue2仍然是主流。我的理解是与时俱进。新一代版本必须跟上语言的特点,必须符合新时代的写作风格。虽然proxy相对于Object.defineProperty做了很多改进,但也不是没有缺点,比如你不兼容IE。世上哪有十全十美的事?友达的勇气,在于放弃一点点现在,换取未来!实现原理了解了背景之后,我们再来复习一下假装代理原理,虽然这个已经被说烂了。但是,写水文要注意什么:两个字——连贯性。constobj={count:1};constproxy=newProxy(obj,{get(target,key,receiver){console.log("Thisisget");returnReflect.get(target,key,receiver);},set(target,key,value,receiver){console.log("hereisset");returnReflect.set(target,key,value,receiver);}});console.log(代理)控制台。log(proxy.count)上面的代码就是Proxy的具体使用方法。配合Reflect,对象的拦截可以如此依赖,实现响应式。可以发现这个obj的整个对象都被拦截了,但是你发现这个对象嵌套的更深一层,比如:constobj={count:1,b:{c:2}};console.log(proxy.b)console.log(proxy.b.c)他就不能再拦截了,我们得把它包裹起来:constobj={a:{count:1}};functionreactive(obj){returnnewProxy(obj,{get(target,key,receiver){console.log("这里是get");//判断一个对象是否被打包一次,实现深度嵌套的响应式if(typeoftarget[key]==="object"){returnreactive(target[key]);};returnReflect.get(目标,密钥,接收者);},set(target,key,value,receiver){console.log("Thisisset");返回Reflect.set(目标、键、值、接收器);}});};constproxy=reactive(obj);好了,原理讲完了,我们正式研究一下下面我列举几个我知道的reactiveloss的例子:props对象的解构,因为会失去响应性React响应对象的直接赋值vuex中的组合API赋值props对象的解构,因为会失去响应性:constobj={a:{count:1},b:1};//reactive是反应式的constproxy=reactive(obj);const{a,b}=代理;console.log(a)console.log(b)console.log(a.count)在上面的代码中,我们发现解构赋值,b不会触发响应式,但是如果访问a会触发响应式它,为什么?别着急,我们一一解释?先讨论一下为什么解构赋值会失去响应性?我们知道解构赋值区分了原始类型赋值和引用类型赋值。原始类型赋值相当于传值,引用类型值相当于传引用,相当于://假设a是一个响应式对象consta={b:1}//c现在是a与当前a无关的valueconstc=a.b//如果直接访问c,相当于直接访问这个值,绕过了a对象的get,就像原文说的失去响应能力一样,那么为什么a有响应性呢?因为a是引用类型,所以我们还记得上面代码中的一个判断吗。如果它是一个对象,它将被重新包装为一个反应类型。由于目前的特性,如果是引用类型,访问内容时不会失去响应性://假设a是一个响应式对象consta={b:{c:3}}//当你访问a.b,响应已经重新初始化。这时候c已经是代理对象了。constc=a.b//直接访问c,相当于访问一个响应式对象,不会失去响应性。以上大致解释了为什么解构赋值可能会失去响应性。估计是文档懒得解释原因,干脆定个规矩,你!别用就好了,你可能会认为是vue的bug,提前改变用户的使用习惯!不习惯直接给reactive对象赋值我们刚开始使用vue3的时候,指定要写如下代码:constvue=reactive({a:1})vue={b:2}然后质疑reactive不是responsive?为什么我赋值后他的反应就消失了,然后他大喊,垃圾vue!其实这是因为你对jsnative的概念还不清楚。事实上,友达已经尽力避免你犯错误。比如由于解构赋值的问题,他直接禁止反应式解构赋值:当你使用解构赋值操作时,他直接禁用。那么又有人问了,为什么props没有禁用呢?因为你的道具??的数据可能没有响应。所以还是那句话:现在的框架呈现其实充满了取舍,有时候真的是两瓶毒药,挑一瓶!回到正题,我们来说说原生的js语法。首先要确认的是,nativejs的引用类型的赋值其实是根据引用地址赋值的!//reactive之后,vue存储了一个返回的代理对象的地址,//打个不恰当的比喻,这个地址是有响应能力的constvue=reactive({a:1})//而当你重新赋值的时候vue,你并没有把新的对象赋给那个地址,而是把vue改成一个新的地址//而这个时候新的地址没有响应,所以就失去了响应能力?vue={b:2}上面就是react失去响应能力的解释,所以这也是很多用户骂人的原因。不符合他的使用习惯。这就是vue2培养出来的一代。在这里我想为游达说句公道话。他没收了你的钱,因为他,你才有饭吃,不能与时俱进,不能拥抱新鲜事物。这是你的无能,这是典型的。端起碗吃肉,放下筷子骂娘。在vuex中组合API赋值也可能在vuex中使用赋值时失去响应能力:statecountincomputedfunction:computed(()=>store.state.count),//accessgetterdoubleincomputedfunction:computed(()=>store.getters.double)}}}在上面的代码中我们找到了store.getters.double必须用computed包裹。其实道理都是一样的,也是变量赋值的原因,这里就不赘述了!最后,这篇文章是,在使用vue3的过程中,挖掘后的一些心得和研究,希望对你有所帮助,让你在工作中升职加薪!