实现vue3中的watch-详细步骤

常用用法

方式一:

image.png

  • 如果state是个对象,那么后续改动这个对象里的属性,回调里拿到的都是新值,也就是说当watch监听对象的时候无法区分前后的新值和老值
  • 这种监控是深度监控,无论state的层数有多深,都会监控到。默认就是深度监控
  • 但是这种监控方式性能不太好,因为如果state对象的层数太深了,每个属性都会收集这个回调函数

方式二:

image.png

  • 这种可以监控一个函数的返回值,函数的返回值一变,就会触发回调函数

watch的本质就是一个effect,内部会对用户填写的数据进行依赖收集

接下来我们就来实现watch

我们先新建一个watch.ts文件,导出watch

image.png

首先,我们需要判断用户传进来的参数是否是响应式的

image.png

  • 具备响应式的值都有ReactiveFlag.IS_REACTIVE标识

image.png

  • 如果用户传进来的是一个响应式的值,那么我们就把它包装成一个函数的返回值
  • 然后写一个函数,返回值是一个对象,对象里会去调回调函数,传入新值和老值,被ReactiveEffect类包装getter后,这个job实际上就是getter的调度器,我们之前的文章有讲过
  • 然后我们拿到ReactiveEffect的实例,最后调run方法,拿到返回值,这个返回值实际上就是getter的返回值,而getter的返回值就是用户传入的数据,而初次调用的时候,拿到的返回值就是用户初始传入的老值
  • 而新值,我们可以在调度器中再执行一次run,也就是再拿一次用户的传入的值,因为调度器是在用户更改数据之后触发的,所以此时取的值一定是新值,然后拿到返回结果作为新值,最后新值和老值作为回调函数的参数
  • 调完回调函数后,新值也就变成了老值

问题1:

  • 我们不能直接把用户传入的值作为函数的返回值,这样只会监控整个对象,没有意义
  • 对用户传入的数据,我们要进行循环,因为只要循环访问到属性,就会触发get,进行依赖收集effect

image.png

image.png

  • 递归进行属性访问,Set防止存在循环引用,值就是对象本身,我们就不用再循环了

处理参数是函数的情况

image.png

  • 如果参数是函数,我们不用特殊处理了

一个实用场景:当用户向输入框输入的时候,我没呢要根据用户输入的内容,返回搜索结果

我们可能想到的写法:

  • watch 输入框的内容,输入框的内容一变化就访问接口,渲染页面

*但是这样会出现一个问题,你先输入一个值,然后再输入一个值,会并发请求两次,第二次有可能会先请求回来,第一次请求的结果会把第二次请求回来的结果干掉

vue3提供了一个机制

伪代码

image.png

  • 第一调用watch的时候,用户注入一个取消的回调,这个回调初始不执行
  • 第二次调用watch的时候会执行第一次注入的回调,会把第一次的clear标识改成true,就会阻止第一次的渲染
  • 在频率非常高的情况下,下一次会阻止上一次的渲染,解决并发问题

现在我们就去实现这个onCleanup

image.png

  • watch方法里增加一个函数onCleanup保存onCleanup回调函数
  • 在调度器中进行判断,因为你下一次更改值后才会触发调度器,也就是说调度器是下一次更改值调用的
  • 在调度器里首先判断cleanup是否有值,有就调用,然后把onCleanup函数传入watch回调函数中,当你调回调函数的时候,会把自己的cleanup传入进去,保存起来。
分类:
前端
标签: