前言


ref和reactive的区别


  • reactive内部采用的proxy,ref内部采用的是defineProperty
  • ref也可以放对象,只是取值的时候需要多取一层,如果是对象用reactive更加合理
  • reactive你如果放普通类型,也就是非对象会直接返回,这个原因可以从我之前的博文中查找
  • ref的出现就是因为reactive处理不了基本类型


 


Ref核心源码


 


第一步


源码目录下创建ref.ts文件,创建ref函数


 


我们接下来要做的第一步就是将传入的普通类型变成一个对象,核心方法是createRef


Vue3源码系列之ref、toRef及toRefs的实现_使用场景


 


插一句,源码写到现在,是不是发现很多方法里面又调其它方法,包了一层,原因就是我们其它的api也可能用到,并且通过传递不同的参数去实现不同的功能。所以说Vue3源码基本上都是高阶函数+科里化


 


第二步


createRef方法接收两个参数,value和是否是浅的


Vue3源码系列之ref、toRef及toRefs的实现_复用_02


 


它内部需要返回一个实例-RefImpl,把参数传进去


Vue3源码系列之ref、toRef及toRefs的实现_使用场景_03


 


RefImpl类


Vue3源码系列之ref、toRef及toRefs的实现_复用_04


 


然后利用类的属性访问器,取值的时候track来进行依赖收集,设置值的时候调用trigger去触发更新


Vue3源码系列之ref、toRef及toRefs的实现_字符串_05


复用了我们之前写reactive时封装的track和tringger方法


 


可能有人会问了,我文章开头的时候说ref底层是利用的defineProperty,那在哪里呢?其实我们刚才写的代码经过babel转化成es5代码就是defineProperty


 


然后我们再去处理下_value


Vue3源码系列之ref、toRef及toRefs的实现_字符串_06


 


当我们取值时取的就是_value


Vue3源码系列之ref、toRef及toRefs的实现_使用场景_07


 


当我们更新值的时候去比较一下新值和老值是否有变化,如果有变化就把新值作为老值


Vue3源码系列之ref、toRef及toRefs的实现_取值_08


 


同时我们还要去把_value给改了,改成newVal,最后再去触发更新


Vue3源码系列之ref、toRef及toRefs的实现_复用_09


 


然后再去处理shallow,如果是深度需要把里面的都编程响应式的


Vue3源码系列之ref、toRef及toRefs的实现_响应式_10


Vue3源码系列之ref、toRef及toRefs的实现_取值_11


 


在我们设置值的时候也要去考虑一下shallow


Vue3源码系列之ref、toRef及toRefs的实现_字符串_12


 


此时你是不是就明白了,如果ref传入多层对象,底层也是会去调用reactive去把它变成响应式


 


 


toRef核心源码


可以把一个对象的值转化成ref类型


 


内部也会产生一个实例,它用的类跟ref不是同一个类


Vue3源码系列之ref、toRef及toRefs的实现_字符串_13


 


ObjectRefImpl类


Vue3源码系列之ref、toRef及toRefs的实现_使用场景_14


相当于我们将某一个key对应的值,转化为ref,就是暴露出一个属性的代理出去,并不会做其它事情,所以这个属性是否是响应式的,取决于代理的对象target是否是响应式的。


 


toRef的使用场景


当我们从一个reactive处理过的对象拿出来某个属性进行操作的时候,你去重新给拿出来的这个属性赋值,并不会产生什么效果,因为拿出来的值就仅仅是个字符串,不会是响应式的。所以我们为了防止拿出来的值是个字符串,我们需要用toRef做一层代理,让


 


假如说我想代理多个呢?toRefs就诞生了,不然你总不能一个一个去代理吧


 


toRefs核心源码


参数可能是对象也可能是数组


Vue3源码系列之ref、toRef及toRefs的实现_使用场景_15


 


有时候我们想把reactive处理过的值结构出来,但是结构出来的数据就不是响应式的了,所以我们需要toRefs处理一下,相当于Vue自己写了个对象解构方法


 


再多唠叨一下toRefs的使用场景


Vue3源码系列之ref、toRef及toRefs的实现_复用_16


我们这样做,在模板中必须得state.来取,不太友好,有点麻烦,我想在模板中直接用,这个时候我们用toRefs


Vue3源码系列之ref、toRef及toRefs的实现_取值_17


这样我们就可以在模板中直接用了