使用Vue3.5的onWatcherCleanup封装自动cancel的fetch函数
前言
在欧阳的上一篇 这应该是全网最详细的Vue3.5版本解读文章中有不少同学对Vue3.5新增的onWatcherCleanup
有点疑惑,这个新增的API好像和watch API
回调的第三个参数onCleanup
功能好像重复了。今天这篇文章来讲讲新增的onWatcherCleanup
函数的使用场景:封装一个自动cancel的fetch函数
。
关注公众号:【前端欧阳】,给自己一个进阶vue的机会
watch回调的第三个参数onCleanup
有些同学可能还不清楚watch
回调的第三个参数onCleanup
,我们先来看个demo,代码如下:
watch(id, (value, oldValue, onCleanup) => {
console.log("do something");
onCleanup(() => {
console.log("cleanup");
});
});
watch
回调的前两个参数大家应该很熟悉,分别是value
新的值,oldValue
旧的值。
第三个参数onCleanup
大家平时可能用的不多,这是一个回调函数,当watch
的值改变后或者组件销毁前就会执行onCleanup
传入的回调。
在上面的demo中就是变量id
改变时会触发onCleanup
中的回调,进而console
打印"cleanup"
字符串。又或者所在的组件销毁前也会触发onCleanup
中的回调,进而console
打印"cleanup"
字符串。
那我们在onCleanup
中可以干嘛呢?
答案是可以清理副作用,比如在watch中使用setInterval
初始化一个定时器。那么我们就可以在onCleanup
的回调中清理掉定时器,无需去组件的beforeUnmount
钩子函数去统一清理。
onWatcherCleanup
函数
onWatcherCleanup
函数的作用和watch
回调的第三个参数onCleanup
差不多,也是当watch
的值改变后或者组件销毁前就会执行onWatcherCleanup
传入的回调。
使用方法也很简单,代码如下:
import { watch, onWatcherCleanup } from "vue";
watch(id, () => {
console.log("do something");
onWatcherCleanup(() => {
console.log("cleanup");
});
});
从上面的代码可以看到onWatcherCleanup
的用法其实和watch
回调的第三个参数onCleanup
差不多,区别在于这里的onWatcherCleanup
是从vue中import导入的。
除了从vue中import导入的区别以外,还有一个区别是onWatcherCleanup
不光在watch
中可以使用,在watchEffect
中同样也可以使用。比如下面这样的:
watchEffect(() => {
console.log("do something in watchEffect", id.value);
onWatcherCleanup(() => {
console.log("cleanup watchEffect");
});
});
和前面的例子一样,上面的代码中id
的值改变后或者组件销毁时也会执行onWatcherCleanup
函数中的console.log
打印。
onWatcherCleanup
函数是从vue中import导入的,那么这意味着onWatcherCleanup
函数的调用可以写在任意地方,只要最终经过函数的层层调用后还是在watch
或者watchEffect
的回调中就可以。
利用上面的这一特点我们可以使用onWatcherCleanup
做到一些onCleanup
做不到的事情,比如:封装一个自动cancel
的fetch
函数。
封装自动cancel的fetch函数
在讲这个之前我们先来了解一下如何cancel
一个fetch
函数。
这里涉及到AbortController
接口,AbortController
接口表示一个控制器对象,允许你根据需要中止一个或多个 Web 请求。
下面这个是cancel
取消一个请求的demo,代码如下:
const controller = new AbortController();
const res = await fetch(url, {
...options,
signal: controller.signal,
});
setTimeout(() => {
controller.abort();
}, 500);
首先使用new AbortController()
创建一个控制器对象controller
。
其中的controller.signal
返回一个 AbortSignal
对象实例,可以用它来和异步操作进行通信或者中止这个操作。
在我们这里把controller.signal
作为signal
选项直接传给fetch函数就可以了。
最后就是可以使用controller.abort()
将fetch请求取消掉,在上面的demo中是如果超过500ms请求还没完成,那么就执行controller.abort()
将fetch请求取消掉。
有了前面的知识铺垫,我们先来看看使用“自动cancel
的fetch
函数”的地方,代码如下:
<script setup lang="ts">
import { watch, ref, watchEffect, onWatcherCleanup } from "vue";
import myFetch from "./myFetch";
const id = ref(1);
const data = ref(null);
watch(id, async () => {
const res = await myFetch(`http://localhost:3000/api/${id.value}`, {
method: "GET",
});
console.log(res);
data.value = res;
});
</script>
<template>
<p>data is: {{ data }}</p>
<button @click="id++">id++</button>
</template>
在上面的例子中使用watch
监听了变量id
,在监听的回调中会使用封装的myFetch
函数请求接口。
上面的例子大家平时应该经常遇到,如果id
的值变化很快,但是服务端接口请求需要2秒才能完成,这时我们期望只有最后一次id
的值改变触发的请求才需要完成,其他请求都cancel取消掉。
如果在myFetch
请求的过程中组件被销毁了,此时我们也期望能够将请求cancel取消掉。
在Vue3.5之前想要去实现上面的这两个需求很麻烦,但是有了Vue3.5的onWatcherCleanup
函数后就非常容易了。
这个是封装的自动cancel
的fetch
函数,myFetch.ts
文件代码如下:
import { getCurrentWatcher, onWatcherCleanup } from "vue";
export default async function myFetch(url: string, options: RequestInit) {
const controller = new AbortController();
if (getCurrentWatcher()) {
onWatcherCleanup(() => {
controller.abort();
});
}
const res = await fetch(url, {
...options,
signal: controller.signal,
});
let json;
try {
json = await res.json();
} catch (error) {
json = {
code: 500,
message: "JSON format error",
};
}
return json;
}
由于onWatcherCleanup
函数是从vue中import导入,那么我们就可以在自己封装的myFetch
函数中导入和使用他。
在onWatcherCleanup
函数的回调中我们执行了controller.abort()
,前面已经讲过了当watch
或者watchEffect
的回调执行前或者组件卸载前就会执行里面的onWatcherCleanup
注册的回调。我们这里的myFetch
是在watch
中调用的,当然也会触发里面的onWatcherCleanup
注册的回调。
在onWatcherCleanup
的回调中执行了controller.abort()
,前面我们讲过了执行controller.abort()
就会将正在请求的fetch函数给cancel取消掉。
就这么简单的就实现了前面的两个需求:
需求一:如果id
的值变化很快,但是服务端接口请求需要2秒才能完成,这时我们期望只有最后一次id
的值改变触发的请求才需要完成,其他请求都cancel取消掉。下面这个是变量id在短时间内多次修改的gif效果图:
从上面的gif图可以看到只有最后一个请求是完成了的,其他请求全部被cancel掉。
需求二:如果在myFetch
请求的过程中组件被销毁了,此时我们也期望能够将请求cancel取消掉。下面这个是组件卸载时gif效果图:
从上图中可以看到在卸载组件时组件正在从服务端请求数据,此时请求会自动cancel掉。
细心的小伙伴发现了在myFetch
函数中,onWatcherCleanup
函数外面套了一个getCurrentWatcher
的判断,代码如下:
import { getCurrentWatcher, onWatcherCleanup } from "vue";
export default async function myFetch(url: string, options: RequestInit) {
// ...省略
if (getCurrentWatcher()) {
onWatcherCleanup(() => {
controller.abort();
});
}
// ...省略
}
当watch或者watchEffect监听的值改变后onWatcherCleanup
的回调就会触发,所以onWatcherCleanup
的执行是由其所在的watch或者watchEffect触发的。
如果onWatcherCleanup
不在watch或者watchEffect的回调中执行,那么当然onWatcherCleanup
中的回调也永远不会执行。
可能有的小伙伴有疑问,你这里的onWatcherCleanup
是在myFetch
中执行的,也没在watch或者watchEffect的回调中执行吖?
答案是myFetch
函数的执行是在watch中执行的,myFetch
然后再去执行onWatcherCleanup
。
而getCurrentWatcher()
函数就会返回当前正在执行回调的watch或者watchEffect,如果当前myFetch
不是在watch或者watchEffect的回调中执行的,那么getCurrentWatcher()
函数的返回值就是空,所以这种情况就不需要去执行onWatcherCleanup
函数了。
最后值得一提的是onWatcherCleanup
不能在await后面执行,比如下面这样的代码:
import { getCurrentWatcher, onWatcherCleanup } from "vue";
export default async function myFetch(url: string, options: RequestInit) {
const controller = new AbortController();
const res = await fetch(url, {
...options,
signal: controller.signal,
});
let json;
try {
json = await res.json();
} catch (error) {
json = {
code: 500,
message: "JSON format error",
};
}
// 错误的写法
if (getCurrentWatcher()) {
onWatcherCleanup(() => {
controller.abort();
});
}
return json;
}
在上面的代码中我们将onWatcherCleanup
调用放在了await fetch()
的后面,这种写法onWatcherCleanup
注册的回调是不会执行的。
为什么在await
后面的onWatcherCleanup
注册的回调永远不会执行呢?
答案是js的await相当于注册了一个回调函数去执行await后的代码,当await等待结束后再去执行这个回调函数,从而执行await后的代码。
await以及之前的代码确实是在watch回调中执行的,我们这里的onWatcherCleanup
就是await后面的代码,await后面的代码是在一个新的回调中执行的,也就是watch“回调中”的“回调中”执行的。
当onWatcherCleanup
执行时已经不知道当前正在执行的watch回调是谁了,所以onWatcherCleanup
的回调也没注册上。当watch的变量修改时或者组件卸载时onWatcherCleanup
注册的回调永远也不会执行。
总结
当watch
或者watchEffect
监听的变量修改时,以及组件卸载时,会去执行他们回调中使用onWatcherCleanup
注册的回调函数。并且onWatcherCleanup
是从vue中import导入的,使得我的可以在任意地方执行onWatcherCleanup
函数。利用这两个特性我们就可以封装一个自动cancel的fetch函数。
关注公众号:【前端欧阳】,给自己一个进阶vue的机会
另外欧阳写了一本开源电子书vue3编译原理揭秘,看完这本书可以让你对vue编译的认知有质的提升。这本书初、中级前端能看懂,完全免费,只求一个star。
使用Vue3.5的onWatcherCleanup封装自动cancel的fetch函数的更多相关文章
- [Javascript] AbortController to cancel the fetch request
We are able to cancel the fetch request by using AbortController with RxJS Observable. return Observ ...
- Vue3 生命周期 && Hooks封装 && toRef
1 # 一.Vue3.0与Vue2.0生命周期改动 2 beforDestroy改名为beforeUnmount 3 destroyed改名为unmounted 4 # Vue3.0页提供了Compo ...
- AngularJS driective 封装 自动滚动插件
1.ui-smooth-scroll.js文件内容 angular.module('app') .directive('uiSmoothScroll', ['$location', '$anchorS ...
- vue3 打开页面input框自动获得焦点
1.需要聚焦的el-input输入框设置ref值: ref="getfcous" <el-input v-model="workorder" ref=&q ...
- Vue3.0工程创建 && setup、ref、reactive函数 && Vue3.0响应式实现原理
1 # 一.创建Vue3.0工程 2 # 1.使用vue-cli创建 3 # 官方文档: https://cli.vuejs.org/zh/guide/creating-a-project.html# ...
- ThinkPHP 3.2.3 自动加载公共函数文件的方法
方法一.加载默认的公共函数文件 在 ThinkPHP 3.2.3 中,默认的公共函数文件位于公共模块 ./Application/Common 下,访问所有的模块之前都会首先加载公共模块下面的配置文件 ...
- ThinkPHP3自动加载公共函数文件
7d 根目录 ├─Application 应用目录 │ ├─Common 公共模块 │ │ ├─Common 公共函数文件目录 │ │ │ ├─index.html │ │ ├─Config 配置文件 ...
- php自动获取字符串编码函数mb_detect_encoding(转)
使用 mb_detect_encoding() 函数来判断字符串是什么编码的. 当在php中使用mb_detect_encoding函数进行编码识别时,很多人都碰到过识别编码有误的问题,例如对与GB2 ...
- php自动获取字符串编码函数mb_detect_encoding
当在php中使用mb_detect_encoding函数进行编码识别时,很多人都碰到过识别编码有误的问题,例如对与GB2312和UTF- 8,或者UTF-8和GBK(这里主要是对于cp936的判断), ...
- PHP目录操作(附封装好的目录操作函数文件)
目录函数库常用API $path='test'; var_dump(is_dir($path));//检测是否为目录 echo '<hr/>'; echo getcwd();//得到当前的 ...
随机推荐
- .NET周刊【7月第1期 2024-07-07】
国内文章 学习.NET 8 MiniApis入门 https://www.cnblogs.com/hejiale010426/p/18280441 MiniApis是ASP.NET Core中的轻量级 ...
- 解码 xsync 的 map 实现
解码 xsync 的 map 实现 最近在寻找 Go 的并发 map 库的时候,翻到一个 github 宝藏库,xsync (https://github.com/puzpuzpuz/xsync) . ...
- C#:只支持GET和POST方法的浏览器,如何发送PUT/DELETE请求?RESTful WebAPI如何响应?
理想的RESTful WebAPI采用面向资源的架构,并使用请求的HTTP方法表示针对目标资源的操作类型.但是理想和现实是有距离的,虽然HTTP协议提供了一系列原生的HTTP方法,但是在具体的网络环境 ...
- Django 解决跨域访问API失败问题
解决跨域访问API失败问题 By:授客 QQ:103355122 实践环境 Win 10 Python 3.5.4 Django-2.0.13.tar.gz 官方下载地址: https://w ...
- CF1883B Chemistry 题解
原题传送门 思路: 如"aba","abba"这样的回文字符串, 每个字符的出现次数有以下两种情况: 1:全部是偶数(abba) 2:只有一个为奇数(aba) ...
- 毕业设计&毕业项目:基于springboot+jsp实现的健身房管理系统
一.前言 在当今数字化时代,音乐已经成为人们生活中不可或缺的一部分.随着技术的飞速发展,构建一个用户友好.功能丰富的在线音乐平台成为了许多开发者和创业者的目标.本文将介绍如何使用SpringBoot作 ...
- centos7更换aliyun软件源一键脚本
centos7更换aliyun软件源 centos7更换aliyun软件源一键脚本 curl -O https://raw.githubusercontent.com/Yogoshiteyo/aliy ...
- Jmeter函数助手6-time
time函数用于获取不同格式的当前时间(年月日时分秒). Format string for SimpleDateFormat (optional):时间格式,填入如yyyyMMdd-HHmmss.d ...
- 【vue3】详解单向数据流,大家千万不用为了某某而某某了。
总览 Vue3 的单向数据流 尽信官网,不如那啥. vue的版本一直在不断更新,内部实现方式也是不断的优化,官网也在不断更新. 既然一切皆在不停地发展,那么我们呢?等着官网更新还是有自己的思考? 我觉 ...
- 【Java】Springboot 响应外切 实现数据脱敏
实现效果: 1.脱敏注解在模型类进行标记 package cn.cloud9.server.test.model; import cn.cloud9.server.struct.masking.ann ...