前言
ref和reactive的区别
-
reactive内部采用的proxy,ref内部采用的是defineProperty
-
ref也可以放对象,只是取值的时候需要多取一层,如果是对象用reactive更加合理
-
reactive你如果放普通类型,也就是非对象会直接返回,这个原因可以从我之前的博文中查找
-
ref的出现就是因为reactive处理不了基本类型
Ref核心源码
第一步
源码目录下创建ref.ts文件,创建ref函数
我们接下来要做的第一步就是将传入的普通类型变成一个对象,核心方法是createRef

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

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

RefImpl类

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

复用了我们之前写reactive时封装的track和tringger方法
可能有人会问了,我文章开头的时候说ref底层是利用的defineProperty,那在哪里呢?其实我们刚才写的代码经过babel转化成es5代码就是defineProperty
然后我们再去处理下_value

当我们取值时取的就是_value

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

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

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


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

此时你是不是就明白了,如果ref传入多层对象,底层也是会去调用reactive去把它变成响应式
toRef核心源码
可以把一个对象的值转化成ref类型
内部也会产生一个实例,它用的类跟ref不是同一个类

ObjectRefImpl类

相当于我们将某一个key对应的值,转化为ref,就是暴露出一个属性的代理出去,并不会做其它事情,所以这个属性是否是响应式的,取决于代理的对象target是否是响应式的。
toRef的使用场景
当我们从一个reactive处理过的对象拿出来某个属性进行操作的时候,你去重新给拿出来的这个属性赋值,并不会产生什么效果,因为拿出来的值就仅仅是个字符串,不会是响应式的。所以我们为了防止拿出来的值是个字符串,我们需要用toRef做一层代理,让
假如说我想代理多个呢?toRefs就诞生了,不然你总不能一个一个去代理吧
toRefs核心源码
参数可能是对象也可能是数组

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

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

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