首页 > 其他分享 >盘点Vue3 watch的一些关键时刻能够大显身手的功能

盘点Vue3 watch的一些关键时刻能够大显身手的功能

时间:2024-11-22 17:14:30浏览次数:3  
标签:count watchHandle stop watch deep 大显身手 Vue3 回调

前言

watch这个API大家应该都不陌生,在Vue3版本中给watch增加不少有用的功能,比如deep选项支持传入数字pause、resume、stop方法once选项onCleanup函数。这些功能大家平时都不怎么用得上,但是在一些特定的场景中,他们能够起大作用,这篇文章欧阳就来带你盘点一下这些功能。

deep支持传入数字

deep选项大家应该比较熟悉,常见的值为true或者false,表示是否深度监听watch传入的对象。

在Vue3.5版本中对deep选项进行了增强,不光支持布尔值,而且还支持传入数字,数字表示需要监听的层数。

比如下面这个例子:

const obj1 = ref({
  a: {
    b: 1,
    c: {
      d: 2,
      e: {
        f: 3,
      },
    },
  },
});

watch(
  obj1,
  () => {
    console.log("监听到obj1变化");
  },
  {
    deep: 3,
  }
);

function changeDeep3Obj() {
  obj1.value.a.c.d = 20;	// 能够触发watch回调
}

function changeDeep4Obj() {
  obj1.value.a.c.e.f = 30;	// 不能触发watch回调
}

在上面的例子watchdeep选项值是3,表明监听到对象的第3层。

changeDeep3Obj函数中就是修改对象的第3层的d属性,所以能够触发watch的回调。

changeDeep4Obj函数是修改对象的第4层的f属性,所以不能触发watch的回调。

他的实现也很简单,我们来看一下deep相关的源码:

function watch(source, cb, options) {
  // ...省略
  if (cb && deep) {
    const depth = deep === true ? Infinity : deep
    getter = () => traverse(baseGetter(), depth)
  }
  // ...省略
}

这里的depth就表示watch监听一个对象的深度。

如果deep选项的值为true,那么就将depth设置为正无穷Infinity,说明需要监听到对象的最深处。

如果deep选项的值为false,或者没有传入deep,那么就表明只需要监听对象的最外层。

如果deep选项的值为number类型数字,那么就把这个数字赋给depth,表明需要监听到对象的具体某一层。

pause、resume、stop方法

这三个方法也是Vue3.5版本中引入的,通过解构watch函数的返回值就可以直接拿到pauseresumestop这三个方法。

我们来看一下源码,其实很简单:

function watch(source, cb, options) {
  // ...省略
  watchHandle.pause = effect.pause.bind(effect)
  watchHandle.resume = effect.resume.bind(effect)
  watchHandle.stop = watchHandle
  return watchHandle
}

watch返回了一个名为watchHandle的对象,对象上面有pause、resume、stop这三个方法,所以我们可以通过解构watch函数的返回值拿到这三个方法。

pause方法的作用是“暂停”watch回调的触发,也就是说在暂停期间不管watch监听的响应式变量如何改变,他的回调函数都不会触发。

有“暂停”,那么肯定就有“恢复”。

resume方法的作用是恢复watch回调的触发,此时会主动执行一次watch的回调。后面watch监听的响应式变量改变时,他的回调函数也会触发。

来看个demo,代码如下:

<template>
  <button @click="count++">count++</button>
  <button @click="runner.pause()">暂停</button>
  <button @click="runner.resume()">恢复</button>
  <button @click="runner.stop()">停止</button>
</template>

<script setup lang="ts">
import { watch, ref } from "vue";

const count = ref(0);
const runner = watch(count, () => {
  console.log(count.value);
});
</script>

点击“count++”按钮会导致watch回调中的console执行。

但是当我们点击了“暂停”按钮后,此时我们再怎么点击“count++”按钮都不会触发watch的回调。

点击恢复按钮后会立即触发一次watch回调的执行,后面点击“count++”按钮也同样会触发watch的回调。

我们来看看pauseresume方法的源码,很简单,代码如下:

class ReactiveEffect {
  pause(): void {
    this.flags |= EffectFlags.PAUSED
  }

  resume(): void {
    if (this.flags & EffectFlags.PAUSED) {
      this.flags &= ~EffectFlags.PAUSED
      if (pausedQueueEffects.has(this)) {
        pausedQueueEffects.delete(this)
        this.trigger()
      }
    }
  }

  trigger(): void {
    if (this.flags & EffectFlags.PAUSED) {
      pausedQueueEffects.add(this)
    } else if (this.scheduler) {
      this.scheduler()
    } else {
      this.runIfDirty()
    }
  }
}

pauseresume方法中通过修改flags属性的值,来切换是不是“暂停状态”。

在执行trigger方法依赖触发时,就会先去读取flags属性判断当前是不是“暂停状态”,如果是那么就不去执行watch的回调。

从上面的代码可以看到这三个方法是在ReactiveEffect类上面的,这个ReactiveEffect类是Vue的一个底层类,watchwatchEffectwatchPosEffectwatchSyncEffect都是基于这个类实现的,所以他们自然也支持pauseresumestop这三个方法。

最后就是stop方法了,当你确定后面都不再想要触发watch的回调了,那么就调用这个stop方法。代码如下:

const watchHandle: WatchHandle = () => {
  effect.stop()
  if (scope && scope.active) {
    remove(scope.effects, effect)
  }
}

watchHandle.stop = watchHandle

响应式变量count收集的订阅者集合中有这个watch回调,所以当count的值改变后会触发watch回调。这里的stop方法中主要是依靠双向链表将这个watch回调从响应式变量count的订阅者集合中给remove掉,所以执行stop方法后无论count变量的值如何改变,watch回调也不会再执行了。

once选项

如果你只想让你的watch回调只执行一次,那么可以试试这个once选项,这个是在Vue3.4版本中新加的。

看个demo:

<template>
  <button @click="count++">count++</button>
</template>

<script setup lang="ts">
import { watch, ref } from "vue";

const count = ref(0);
watch(
  count,
  () => {
    console.log("once", count.value);
  },
  {
    once: true,
  }
);
</script>

由于使用了once选项,所以只有第一次点击“count++”按钮才会触发watch的回调。后面再怎么点击按钮都不会触发watch回调。

我们来看看once选项的源码,很简单,代码如下:

function watch(source, cb, options) {
  const watchHandle: WatchHandle = () => {
    effect.stop()
    if (scope && scope.active) {
      remove(scope.effects, effect)
    }
  }

  if (once && cb) {
    const _cb = cb
    cb = (...args) => {
      _cb(...args)
      watchHandle()
    }
  }

  // ...省略
  watchHandle.pause = effect.pause.bind(effect)
  watchHandle.resume = effect.resume.bind(effect)
  watchHandle.stop = watchHandle
  return watchHandle
}

先看中间的代码if (once && cb),这句话的意思是如果once选项的值为true,并且也传入了watch回调。那么就封装一层新的cb回调函数,在新的回调函数中还是会执行用户传入的watch回调。然后再去执行一个watchHandle函数,这个watchHandle是不是觉得有点眼熟?

前面讲的stop方法其实就是在执行这个watchHandle,执行完这个watchHandle函数后watch就不再监听count变量了,所以后续不管count变量怎么修改,watch的回调也不会再触发。

onCleanup函数

有的情况我们需要watch监听一个变量,然后去发起http请求。如果变量改变的很快就会出现第一个请求还没回来,第二个请求就已经发起了。在一些极端情况下还会出现第一个请求的响应比第二个请求的响应还要慢,此时第一个请求的返回值就会覆盖第二个请求的返回值。实际上我们期待最终拿到的是第二个请求的返回值。

这种情况我们就可以使用onCleanup函数,他是作为watch回调的第三个参数暴露给我们的。看个例子:

watch(id, async (newId, oldId, onCleanup) => {
  const { response, cancel } = myFetch(newId)
  // 当 `id` 变化时,`cancel` 将被调用,
  // 取消之前的未完成的请求
  onCleanup(cancel)
  data.value = await response
})

watch回调的前两个参数大家都很熟悉:新的id值和旧的id值。第三个参数就是onCleanup函数,在watch回调触发之前调用,所以我们可以使用他来cancel掉上一次的请求。

onCleanup函数的注册也很简单,代码如下:

let boundCleanup

boundCleanup = fn => onWatcherCleanup(fn, false, effect)

function watch(source, cb, options) {
  // ...省略
  const job = (immediateFirstRun?: boolean) => {
    const args = [
      newValue,
      oldValue,
      boundCleanup,
    ]
    cb(...args)
    oldValue = newValue
  }
  // ...省略
}

执行watch回调实际就是在执行这个job函数,在job函数中执行watch回调时传入了三个参数。分别是newValueoldValueboundCleanup。前两个参数大家都很熟悉,第三个参数boundCleanup是一个函数:fn => onWatcherCleanup(fn, false, effect)

这个onWatcherCleanup大家熟悉不?这也是Vue暴露出来的一个API,注册一个清理函数,在当前侦听器即将重新运行时执行。

总结

这篇文章盘点了Vue3 watch新增的一些新功能:deep选项支持传入数字pause、resume、stop方法once选项onCleanup函数。这些功能大家平时可能用不上,但是还是要知道有这些功能,因为有的情况下这些功能能够派上大用场。

文章转载自:前端欧阳

原文链接:https://www.cnblogs.com/heavenYJJ/p/18560249

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

标签:count,watchHandle,stop,watch,deep,大显身手,Vue3,回调
From: https://blog.csdn.net/kfashfasf/article/details/143966051

相关文章

  • SpringBoot3+Vue3+AntDesign电商后台管理系统 | 小蚂蚁云
     项目介绍基于SpringBoot3、SpringSecurity、MybatisPlus、Vue3、TypeScript、Vite、AntDesign、MySQL等技术栈实现的单体前后端分离后台管理系统;后端基于Java语言采用SpringBoot3、SpringSecurity、MybatisPlus、MySQL等主流技术栈,前端基于Vue3、TypeScript、Vite等技术栈实......
  • SpringBoot3+Vue3+AntDesign单体多模块项目实践 | 小蚂蚁云
     项目介绍基于SpringBoot3、SpringSecurity、MybatisPlus、Vue3、TypeScript、Vite、AntDesign、MySQL等技术栈实现的单体前后端分离后台管理系统;后端基于Java语言采用SpringBoot3、SpringSecurity、MybatisPlus、MySQL等主流技术栈,前端基于Vue3、TypeScript、Vite等技术栈实......
  • SpringBoot3+Vue3+AntDesign博客后台管理系统源码 | 小蚂蚁云
     项目介绍基于SpringBoot3、SpringSecurity、MybatisPlus、Vue3、TypeScript、Vite、AntDesign、MySQL等技术栈实现的单体前后端分离后台管理系统;后端基于Java语言采用SpringBoot3、SpringSecurity、MybatisPlus、MySQL等主流技术栈,前端基于Vue3、TypeScript、Vite等技术栈实......
  • vue2项目升级至vue3方案步骤踩坑
    背景这是一个金融类的项目:营销平台,项目是vuecli4搭建的vue2+elementUI+vant+antvPC端项目,需要内网开发共有**60万行代码**,1720个vue文件,使用了一些只能适配vue2的依赖库和技术,需要找替代库和替代方案一把项目备份然后使用gogocode进行转换先将源代码进行格式化转换,方便以......
  • vue3+setup使用rtsp视频流实现实时监控,全屏,拍摄,自动拍摄等功能(纯前端)
    vue3+setup使用rtsp视频流实现实时监控,全屏,拍摄,自动拍摄等功能(纯前端)概要本文介绍了如何在Vue应用中通过WebRTC技术获取摄像头的rtsp视频流,同时展示了实时监控,全屏,拍摄,自动拍摄等功能。一、获取rtsp流并确保其可用1.因为是纯前端角度,所以从后端可以获取http开头的视频......
  • 【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
    大家好,欢迎来到程序视点!我是小二哥!前言在VUE项目开发中,一些数据常常被多个组件频繁使用,为了管理和维护这些数据,就出现了状态管理模式。今天小二哥要给大家推荐的不是VueX,而是称为新一代的状态管理工具的Pinia.js。   关于Pinia.jsPinia.js由Vue......
  • 基于vue3的权限控制实现
    需求:不同角色的用户显示不同的菜单,且对页面的按钮也有是否对角色开发的权限服务端返回两个数据一个是菜单的权限列表,另外一个是拥有的按钮列表数据,如下: 动态加载菜单/**routerInfo:动态路由hasRoutePermission:用户拥有的路由权限*/constfilterDeepDynamic......
  • vue3版本实现h5自适应布局
    amfe-flexible和postcss-pxtorem可以一起使用来实现移动端的适配效果。参考的页面地址vite.config.js配置importpxtoremfrom'postcss-pxtorem';exportdefaultdefineConfig({plugins:[vue()],css:{postcss:{plugins:[pxtore......
  • 推荐 vue2、vue3 中功能最强大的表格组件,性能最强大的表格组件推荐、可编辑表格推荐
    vxe-table是一个vue的表格组件,支持可编辑和虚拟滚动高性能表格,公司使用了几年的表格,grid渲染器扩展功能非常强大。[email protected]@4.9.3//...importVxeUIfrom'vxe-pc-ui'import'vxe-pc-ui/lib/style.css'importVxeUITablefrom'vxe-table'......
  • 盘点Vue3 watch的一些关键时刻能够大显身手的功能
    前言watch这个API大家应该都不陌生,在Vue3版本中给watch增加不少有用的功能,比如deep选项支持传入数字、pause、resume、stop方法、once选项、onCleanup函数。这些功能大家平时都不怎么用得上,但是在一些特定的场景中,他们能够起大作用,这篇文章欧阳就来带你盘点一下这些功能。关注公......