课程地址:【已完结】全网最详细Vue3源码解析!(一行行带你手写Vue3源码)
第二部分-实现响应式(5):(对应课程的第18-21节)
第18节:《创建ref实例》
vue3中 ref 的作用是将普通类型的数据实现代理,其原理是Object.defineProperty() 。
1、首先,在源码中使用ref并总结其特性;观察打印出来的被ref()加工后的原始字符串值:
2、在 reactivity 文件夹下创建ref.ts 文件,在其中定义并暴露 ref 与 shallowRef 方法,这两个方法都接收目标对象target,其内部都返回一个 createRef() 方法;定义 createRef 方法,其返回一个 RefImpl 的实例对象(refImpl是 reference+implement,译为引用实现):
3、定义 RefImpl 类:
4、将形参 target 改为 rawValue :
5、在入口文件中引入并暴露 ref 方法:
6、examples文件夹中新建3.ref.html 文件,测试刚刚写的ref方法:
第19节:《实现ref》
1、引入 Track与trigger方法,引入操作符以及 hasChange 方法::
2、在类的构造函数中,将用户传入的字符串保存到实例的 _value 属性;定义类的属性访问器:get函数与set函数。在对实例的value的get函数中,Track方法实现收集依赖,并返回用户传入的原始值;set函数中,判断新值与旧值相比是否改变,如果改变则将新值赋值覆盖旧值,并触发trigger方法,用来触发更新:
3、测试 ref 方法,调试错误,发现判断新值与旧值是否相等的条件中需要取反:
修改问题后,页面实现了相应的效果,初始展示出了“张三”,1秒后变成了“555”。说明编写的ref方法奏效了。
疑问:为什么说 ref() 方法的实现原理是 Object.defineProperty() ?
第20节:《实现toRef》
1、在源码中使用 toRef() :
2、在 ref.ts 文件中增加实现 toRef 方法的逻辑:
3、入口文件中引入并导出 toRef 方法:
4、examples文件夹下新建 4.toRef.html 文件,在其中测试刚刚编写的 toRef 方法。会发现,到现在我们只是实现了通过 toRef 将对象的某个值变成一个实例,并将其值放到实例的 value 属性中,但我们并没有实现这个属性值的响应式,在定时器中改变这个值时,界面并不会改变:
5、当 state 本身是用reactive 定义的响应式数据时,通过 toRef 将其某个属性值变成一个 ref 实例,此时得到的该值就是响应式的;反之,当 state 本身只是一个普通对象,并不是响应式对象时,通过 toRef 将其某个属性值变成一个 ref 实例,此时得到的该值也不是响应式的。
个人理解:因为当 state 本身是用reactive 定义的响应式数据时,通过 toRef 将其某个属性值变成一个 ref 实例,在以上我们的实现中,定义 ObjectRefImpl 类时,get与set方法中,分别有读取和设置target对象,也就是目标对象state的这个属性值的行为,当目标对象state本身是一个响应式对象时,读取和设置其属性值时,就会触发相应的依赖收集(Track)与触发更新(trigger)的相关逻辑,所以此时就会是响应式的。
第21节:《实现toRefs》
1、在源码中使用 toRefs:
2、在ref.ts中定义 toRefs 方法:遍历这个target对象,并将其中每一个属性(或元素)用 toRef 方法处理成一个 ref对象:
3、在入口文件中暴露此方法:
4、在 examples 下新建 5.toRefs.html 文件,在其中测试刚刚编写的 toRefs 方法,可以打印出与源码相同的结构:
5、在工作中经常这样使用:用 toRefs 将一个响应式对象的所有属性都转变成 ref 对象,这样在视图中就可以直接使用这些属性了: