全面解析Vue3Reactive家族和Ref家族API_第1页
全面解析Vue3Reactive家族和Ref家族API_第2页
全面解析Vue3Reactive家族和Ref家族API_第3页
全面解析Vue3Reactive家族和Ref家族API_第4页
全面解析Vue3Reactive家族和Ref家族API_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、全面解析Vue3Reactive家族和Ref家族API刖弓你是不是习惯了Vue2的赋值即响应式?Vue2还有个Vue.observable但你从没用过?结果Vue3像跳跳糖一样跳出来这么多的响应式API,你有没有懵逼的感觉?不慌,挨个学。由于官方文档写的晦涩难懂,所以我写下这篇。原创:简书microkof。首先说明,全文提到的“基本数据”是指“数据类型为基本数据类型的数据”,“原始数据”是指被转变为响应式数据之前的纯对象或基本数据。reactiveVue3的根基。返回对象的响应式副本,响应式转换是“深层”的一一它影响所有嵌套property。返回Proxy对象,不等于原始对象。建议只操作Pro

2、xy对象,不要操作原始对象。官方建议,对来自于服务器的数据或者注定要响应式的数据执行reactive之前,最好不要用临时变量储存原始数据,因为没有意义,而且两个变量容易让初学者引起误操作。countis:r.aimportreactivefromvue;exportdefaultsetup()letr=reactive(a:1);returnr,;,;refref说白了就是reactive(vaiue:原始数据)。下方代码如果打印r对象,会得到Reflmpl(ref)对象,它有一个value属性指向基础类型值30。countis:rimportreffromvue;exportdefaults

3、etup()letr=ref(30);returnr,;,;为什么似乎Proxy已经解决所有问题,还要有refAPI呢?因为ES的ProxyAPI是为引用数据类型服务的,它无法为基本数据类型提供代理。如果强行代理,Vue会有提示:valuecannotbemadereactive:30。那么为什么Vue2的defineproperty并没有区分基本数据类型和引用数据类型呢?因为defineproperty就是Object的静态方法,它只是为对象服务的,甚至无法对数组服务,因此Vue2弄了一个data根对象来存放基本数据类型,这样无论什么类型,都是根对象的property,所以也就能代理基本数据

4、类型。而Proxy能对所有引用类型代理,Vue3也不再用data根对象,而是一个个的变量,所以带来了新问题,如何代理基本数据类型呢?并没有原生办法,只能构建一个value:ProxyObject结构的对象,这样Proxy也就能代理了。问题来了,同样是响应式结构,ref跟reactive的区别是什么?ref与reactive的区另U对比refreactive返回数据类型RefImpl对象(也叫ref对象)Proxy对象传入基本类型返回value:基本类型禁止这么做传入引用类型返回value:Proxy对象Proxy对象两者分别适用场合:ref可以为基本类型添加响应式,也可以为引用类型添加响应式r

5、eactive只能为引用类型添加响应式。对于引用类型,什么时候用ref,什么时候用reactive?简单说,如果你只打算修改引用类型的一个属性,那么推荐用reactive,如果你打算变量重赋值,那么一定要用ref。具体见下文。使用组合式API的话,请一定了解“重赋值自身”和“重赋值自身属性”的区别这一点非常重要。先看配置项式API中重赋值Proxy的范例,跟Vue2没有区别,页面会渲染出name:赵六,age:21字符:jsonDataexportdefaultdata()returnjsonData:name:”牛二,age:13,,;,created。this.jsonData=name:

6、王五,age:19,;,mounted。this.jsonData=name:赵六,age:21,;,;而使用组合式API,你会发现reactive后的Proxy在onMounted中重赋值无法触发渲染,最终页面显示“name:王五”,age:19而不是赵六:jsonDataimportonMounted,reactivefromvue;exportdefaultsetup()letjsonData=reactive(name:牛二,age:13,);jsonData=reactive(name:王五,age:19,);onMounted()=jsonData=reactive(name:赵六

7、,age:100,););returnjsonData,;,;原因在于jsonData尽管是响应式的,但是响应式的是它的属性,而不是它自身,重赋值它自身跟重赋值它的属性是两码事。所以,想在组合式API中让数据具备响应式,必须用ref,因为ref又对Proxy包装了一层,修改ref其实是修改它的value,它的value定是响应式的,因此视图就正常更新了。再多说一点,如果数据是服务器返回的LIST数据,而且只显示、不变更,那么最好是使用shallowRef来包装数据,可以节能。如果会有变更,那么应该用ref。下例中采用了Ref语法糖。页面会显示Michael的信息:jsonDataitem.na

8、me-item.ageimportonMounted,reffromvue;ref:jsonData=name:Jim,age:13,;jsonData=name:Tom,age:19,;onMounted()=jsonData=name:Michael,age:100,;);现在好像ref把引用数据类型也管起来了,到底啥时候才适合用reactive呢?很简单啊,如果你确信你只可能去改引用类型数据的属性,那么一定要用reactive,如果还有可能要整体重赋值,那还得用ref。所以说:需要在组合式API里给变量重赋值的话,无论什么数据类型都必须用ref,不可以用reactive。到此,我们清楚了

9、ref与reactive都必须存在的理由,接着说,reactive有一套兄弟API,ref也有一套,它们都是干什么用的?先看reactive的:reactive与shallowReactive的区另U打印的话,乍一看没有区别,但是shallow的中文意义是浅层的”,shallowReactive不代理深层property,只会指向原始对象的深层property。注意,给shallowReactive传入Proxy是没有意义的,即便这么做,直接返回该Proxy。shallowReactive的用途是:如果一个对象的深层不可能变化,那么就没必要深层响应,这时候用shallowReactive可以节

10、省系统开销。下例中,按下第2个button不会有反应,只有又去按下第1个button之后,视图刷新,第二个button才有反应。countis:r.b.ccountis:s.b.cimportreactive,shallowReactivefromvue;exportdefaultsetup()letr=reactive(a:1,b:c:2);console.log(r);lets=shallowReactive(a:1,b:c:2);consoleog(s);returnr,s;,;reactive与readonly的区另Ureactive般只接受ES普通的引用数据类型,尽管它也可以接受Pr

11、oxy对象,但是没有意义、没有必要,但readonly可以接受Proxy对象,而且有实际意义,它可以获取纯对象或者Proxy或者Reflmpl,返回原始代理的只读代理。说白了它做2步操作,先reactive,然后另生成一个只读Proxy。readonly的只读是深层的只读:访问的任何嵌套property也是只读的。readonly存在的意义有2个,一个是保护数据不被修改,另一个是提升性能。下例中,第2个button点击不会有反应。countis:r.b.ccountis:s.b.cimportreactive,readonlyfromvue;exportdefaultsetup()letr=r

12、eactive(a:1,b:c:2);console.log(r);lets=readonly(a:1,b:c:2);consoleog(s);returnr,s;,;readonly与shallowReadonly的区另U就像reactive与shallowReactive的一样,shallowReadonly只会给对象的第一层property设置只读,不去管深层property,因此深层property并没有被代理,只会指向原始对象。下例中:按下buttonl会有报错提示:Setoperationonkeycfailed:targetisreadonly.,因为是深层只读的。按下butto

13、n2没有任何反应,因为shallowReadonly的深层是指向原始值的,修改原始对象不会反映到视图上。按下button3也会有报错提示:Setoperationonkeyafailed:targetisreadonly.,因为shallowReadonly是浅层只读的,a恰好是浅层property。countis:r.b.ccountis:s.b.ccountis:s.aimportreadonly,shallowReadonlyfromvue;exportdefaultsetup()letr=readonly(a:1,b:c:2);consoleog(r);lets=shallowRead

14、only(a:1,b:c:2);consoleog(s);returnr,s;,;shallowReactive与shallowReadonly的区另U首先说,两者对深层property的态度是一致的,即不去代理,深层property都是指向原始对象的深层property,都允许直接修的深层property,区别在于对待浅层property方面。shallowReactive允许修改浅层property,shallowReadonly不允许,Vue3会阻止并给出报错。isReactive、isReadonly、isProxy的区另UisReactive:Proxy是否是由reactive创建,

15、是则返回trueisReadonly:Proxy由readonly创建则返回trueisProxy:上面两个满足任意一条,就返回true上例中,如果加入这3条打印会得到什么?console.log(isReadonly(s);/trueconsole.log(isReadonly(s.a);/false,因为s.a得到的是a的值,而不是a自身,a的值当然不是响应式的console.log(isReadonly(s.b);/false,道理同上”:F+!-s:-:!+!-S:-:r:-:!+!:-:F-t-t:-:!:+:!:-:F-t-t:-:!:+:!:-:F-t-t:-:!:+:!:-:F

16、-t-t:-:!:+:!:-:F-t-t:-:!:+:!:-:F-t-t:-:!:+:!:-:F-t-t:-:!:+:!:-:F-t-t:*:!:-:F-t-t:*:!:-:F-t-t:*:!:-:F-t-t:*:!:-:F-t-t:*:!:-:F-t-t:*:!:-:F-t-t:*:!:-:F-t-t:*:!:-:F-t-t:*:!:-:F-t-t:*:!:-:F-t-t:*:!:-:F-t-t:*:!toRaw是什么官方已经解释的很清楚,返回proxy的原始对象。这是一个转义口,2个作用:可用于临时读取而不会引起proxy访问/跟踪开销,也可用于写入而不会触发视图更新。官方又说,不建议保留

17、对原始对象的持久引用。请谨慎使用。这句话什么意思?就是说:1.尽量不要把原始对象赋值给变量,尽量减少中间变量;2将原始对象转换为Proxy之后,如果你临时打算操作一下原始对象,那么也不要因为这个目的就早早的把原始对象赋值给变量,而是应该用toRaw(proxy),以获取原始对象,比如得到一个变量R,然后你可以操作R,操作完成之后就不要再碰R,而应继续操作Proxy。下例中:按下buttonl,会发现button2也跟着变,这表明Proxy的基本原理:操作Proxy会反映到原始对象身上。按下button2,没有任何反应,表明操作原始对象不会反映到视图上。这时候重新按下buttonl,会发现数字跳

18、跃了几个数,这表明直接修改原始对象之后,Proxy对原始对象继续代理,并不需要重新reactive。countis:r.b.ccountis:s.b.cimportreactive,toRawfromvue;exportdefaultsetup()letr=reactive(a:1,b:c:2);consoleog(r);lets=toRaw(r);consoleog(s);returnr,s;,;妙mm:J-;fiSwSw;wimarkRaw与readonly的区别markRaw是操作原始对象的,它的意义是将原始对象或者原始对象的某个浅层或深层property标记为“永远不允许被代理”。Vu

19、e3会给对象的第一层或某深层加一个标记_v_skip:true,这样,即便原始对象被reactive之后,得到的该层和更深层就不会被代理。如果想给原始对象的某个property加markRaw,需要执行3步,先定义变量指向该property,然后markRaw这个变量得到新对象,然后让源对象的property指向新对象。如果将加了标记的原始对象当做其他原始对象的属性,其他原始对象被reactive之后,加了标记的对象依然不会被reactive。也就是说reactive见了vskip:true就绕着走。注意:虽然Vue3只会在表层加标记,但是会影响深层的property。markRaw与read

20、only的区别,在于侧重点不同:markRaw允许被修改,但不允许被代理。这里尽管说允许修改,但是修改的意义不大,毕竟Vue的核心思想是响应式,在添加响应式之前修改意义不大。readonly不允许被修改,但已经被代理。它们两者相同点在于,从不同角度节省系统开销。markRaw的用途:首先说,直接给某个对象全盘markRaw是没有意义的,因为你就是开发者,你不想让某对象被reactive,那么你不去写reactive就好了啊。所以markRaw的用途应该是:允许对象被reactive,但是阻止对象的部分内容被reactive。markRaw与shallowReactive的区另UmarkRaws

21、hallowReactive作用阻止响应式让浅层property响应式,不操作深层property浅层property阻止响应式执行响应式深层property阻止响应式不执行响应式,也不阻止如果希望阻止其他程序员将对象响应式,则可以使用markRaw来保护。如果刚好打算不让从第2层到最深层的所有property响应式,那么用shallowReactive可能更好。如果想要更定制化的阻止某些property被响应式,那么应当使用markRaw。例如下例中,x.b被标记,然后重复值给自己,此时再将x响应式,x.b依然没有被响应式。所以点击button不会有反应。countis:s.b.cimpor

22、tmarkRaw,reactivefromvue;exportdefaultsetup()letx=a:1,b:c:2;x.b=markRaw(x.b);lets=reactive(x);consoleog(isReactive(s);/trueconsole。g(isReactive(s.b);/falsereturns;,;.C::蝕洋:撅fg:蝕皱:!:虫撅!富:蝕皱甘虫:蝕皱撅ft:!:烛竝!甘虫爲:蝕!*撅f:蝕皱甘虫!:虫虫姣:蝕皱竝冬:越!能$?8:越!能鮫:!:烛控控控W:越!能竝g:越!能鮫:虫控控W:越!能控8:越!能搀!:虫姣空主W:越!能控8:越!能竝g:越虫:AJAA

23、AAAJ最后我们看ref和它的兄弟API。ref与shallowRef的区别refshallowRef本质reactive(value:原始数据)shallowReactive(value:原始数据)区别点value:原始数据被深层响应式只有value被响应式,原始数据没有响应式传入基本类型两个API无差别两个API无差别,性能考虑尽量用shallowRef传入引用类型value指向Proxyvalue指向原始数据shallowRef的作用是只对value添加响应式,因此,必须是value被重新赋值才会触发响应式shallowRef的出现主要是为了节省系统开销。下例中,点击buttonl会有反

24、应,点击button2不会有反应。关键是点击button3,我们知道在template里,如果给s重新赋值,其实相当于给s.value重新赋值,由于value是响应式的,这时候button2和button3都会有变化。countis:r.b.ccountis:s.b.ccountis:s.b.cimportref,shallowReffromvue;exportdefaultsetup()letr=ref(a:1,b:c:2);lets=shallowRef(a:1,b:c:2);returnr,s,;,;toRef是咋回事先看看这个题目,看看Proxy对象里面的基本数据是否具备响应式:cou

25、ntis:r.acountis:simportreactive,toReffromvue;exportdefaultsetup()letr=reactive(a:1);consoleog(r);lets=r.a;consoleog(s);returnr,s;,;当我点击button1的时候,你说button2会变吗?并不会。变量s就是个基本数据,没有任何响应式。很不爽是不是?现在我改改,把濱s=r.a;改成lets=toRef(r,a);,然后再试试?可以看到button2的数字跟着变了!这就是toRef的作用:当一个变量指向一个对象的某个property,且这个property是基本数据类型

26、时,必须用toRef才能变量与对象的响应式连接。如果这个property是引用数据类型,就不需要动用toRef。toRef的用途之一是用于传参,可传递一个响应式的基本数据类型。toRef还有一个特点是可以提前绑定,看个例子,r的原始数据并没有property叫c,但是我就任性,我就提前让s赋值为toRef(r,c),这时候两个button上是没有数据的,毕竟propertyc是不存在的,在我点击buttonl之后,两个button都显示了3,说明提前绑定是有用的。countis:r.ccountis:simportreactive,toReffromvue;exportdefaultsetup

27、()letr=reactive(a:b:2);console.log(r);lets=toRef(r,c);consoleog(s);returnr,s;,;ref与toRef的区别r用法返回误传应要不传应要不区直原Opr讲解一下误区。比如下例中:点击buttonl,打印的ref对象是如期待的count:4,视图也更新为4,但是原始值并没有变,依然是count:3。这说明:给ref传纯对象的属性会造成困惑。刷新页面,只点击button2,打印的ref对象变了,原始值也变了,但是视图没有更新,还是3。说明:给toRef传入原始值是错误的操作,应当传入Proxy,但也证明toRef对传入值是指向关

28、系。刷新页面,只点击button3,切如期待,说明:上面的说法是正确的。statel增加state2增加state3.a-state4增加importreactive,ref,toReffromvue;exportdefaultsetup()constobj=count:3;conststate1=ref(obj.count);conststate2=toRef(obj,count);conststate3=reactive(a:5);conststate4=toRef(state3,a);functionadd1()state1.value+;console.log(原始值obj:,obj)

29、;consoleog(state1:,state1);functionadd2()state2.value+;console.log(原始值obj:,obj);console.log(state2:,state2);functionadd3()state4.value+;consoleog(state3:,state3);consoleog(state4:,state4);returnstate1,state2,state3,state4,add1,add2,add3;,;toRef与toRefs的区别toRefs可以看做批量版本的toRefotoReftoRefs用法toRef(Proxy,

30、xxprop)toRefs(Proxy)返回ObjectReflmp1对象同左作用创建变量到Proxy属性的响应式连接创建变量每个属性到Proxy每个属性的响应式连接连接关系一对一多对多下例中,当点击buttonl时,所有button都会有反应。countis:r.ccountis:scountis:t.c.valueimportreactive,toRef,toRefsfromvue;exportdefaultsetup()letr=reactive(a:b:2,c:4);consoleog(r);lets=toRef(r,c);consoleog(s);lett=toRefs(r);con

31、soleog(t.c)returnr,s,t;,;toRefs的一大用途是变相解构Proxy。首先了解一个常识,Proxy如果解构,基本数据会丢失响应式。现在我既想要解构Proxy,又不想丢失响应式,怎么办?可以使用toRefs。下例中,变量c是基本数据,它不具备响应式,因此buttonl被点击之后,button2不会跟着变。如果将letc=r;改成letc=toRefs(r);,则变量c具备了响应式,button2会跟着变。countis:r.ccountis:cimportreactive,toRefsfromvue;exportdefaultsetup()letr=reactive(a:

32、b:2,c:4);letc=r;returnr,c;,;toRefs的简洁用法:return.toRefs(Proxy),others可用于返回解构的Proxy,而不需要创建临时变量。如果组件只需要返回一个解构的Proxy,可以更简略:returntoRefs(Proxy)。customRef是什么customRef跟ref、toRef、toRefs有很大区别,它生成的ref对象会自定义get和set。customRef的主要用途至少有2个:时机上说,可以控制视图更新的时机,可以延迟更新。其他ref兄弟API都做不到。内容上说,可以修改传入的原始数据,让原始数据与返回值不相同。其他兄弟API也

33、做不到。text-functionuseDebouncedRef(value,delay=200)lettimeoutreturncustomRef(track,trigger)=returnget()track()returnvalue,set(newValue)clearTimeout(timeout)timeout=setTimeout()=value=newValue+1console.log(value)trigger(),delay)exportdefaultsetup()returntext:useDebouncedRef(hello):絕澱邂題巫縛總澱芟邂&翅总澱瀚邂&總趣磁邂

34、澱芟轉第縛邂&滋邂&耀芟邂耀澱遂統&邂澱邂禺邂&邈統&邂总澱芟邂&邂邂澱克邂澱瀚&曲嚥綴妙做&拐交曲空&於&曲空&拐交曲嚥綴&曲嚥綴妙做&拐交壮朋綴&曲空&拐交曲空&期&曲嚥锻曲披朋锻曲披朋锻縈做:锻曲披朋:&A壮曲空&於披朋锻曲郴嚥锻曲郴空&扯辙禎&扯辙赠:做:测试:在input里快速敲入一串字符,左边的text位置会延迟出现结果,而且会节流,而且console.log(value)也会延迟打印。解释:track和trigger,其中track用于追踪,写在return之前trigger是触发,用在赋值给value语句之后。将ref改写成CUStomRef应该怎么写?去掉延时,且value=

35、newValue即可。unref是什么unref类似于toRawunref的本质就是解包,把value:Proxy|基本数据解成Proxy|基本数据unref对toRefs创造的对象的各个属性也起作用,因为各个属性也是ref对象。由于Vue3的开发原则是尽量不要直接修改内部值,对ref来讲就是尽量不要修改Proxy,如果某些场景下非要直接修改Proxy,需要用unref临时将ref还原为Proxy。与toRaw样,修改完Proxy之后并不需要重新执行ref。下例中,点击button1和button2,两个按钮都会有反应。countis:r.b.ccountis:s.b.cimportref,unreffromvue;exportdefaultsetup()letr=ref(a:1,b:c:2);lets=unref(r

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论