本文正在参加「金石计划」
在我们项目开发的时候,很多时候都会用到节流和防抖来防止用户频繁操作从而造成资源与服务器的浪费。所以作为一个前端我们往往需要在合适的位置加上防抖或者节流。本篇文章将介绍如何使用 Vue3 自定义指令的方式实现元素点击节流指令v-throttleClick
和输入框的防抖指令v-debounceInput
防抖和节流的定义
在开始之前,我们先了解一下什么是防抖,什么是节流。
防抖是指当你触发一个事件的时候,它的回调不会立即执行,而是在 n 秒之后执行,但当在这 n 秒之内你又触发了这个事件,则会重新计时,也就是你还需要再等 n 秒才会执行。
节流是指当你触发一个事件的时候,它会立刻执行回调,但是如果你在 n 秒之内再次触发,回调不再执行,也就是说单位时间内触发事件只会执行一次回调
了解了防抖和节流之再看如何实现v-throttleClick
和v-debounceInput
元素点击节流:v-throttleClick
我们先看下v-throttleClick
要实现的效果:指令接受一个回调函数,当给元素绑定这个指令的时候,点击该元素则会以节流的形式触发改回调函数。同时接收一个 arg 作为节流事件,默认 1000ms
实现起来其实很简单
<template>
<button v-throttleClick:2000="kfc50">点击V我50吃鸡脚</button>
</template>
<script setup>
const vThrottleClick = (el, bind) => {
let isPass = true;
el.addEventListener("click", () => {
if (typeof bind.value !== "function") return;
if (isPass) {
bind.value();
isPass = false;
setTimeout(() => {
isPass = true;
}, bind.arg || 1000);
}
});
};
const kfc50 = () => {
console.log("+50");
};
</script>
然后我们频繁点击按钮看效果
但是如果我们直接在回调函数中传参,函数就会立即执行,比如
<button v-throttleClick:2000="kfc50(1)">点击V我50吃鸡脚</button>
并且点击之后也不会进入逻辑判断,因为 bind.value 不再是个函数。所以如果有传参需求,则需要将回调写成高阶函数形式
const kfc50 = (arg1) => {
return () => {
console.log(arg1);
};
};
到这里一个简单的v-throttleClic
指令就完成了。那么它可以用作什么场景呢? 比如你有一个搜索按钮,你肯定不能在用户疯狂点击的时候一直发请求吧,那就加一个 v-throttleClick 指令吧
输入框防抖v-debounceInput
假如你有这样一个需求,用户不停的在输入框输入内容,当用户停止输入的时候再触发搜索向后台发送请求。这时候聪明的你一定想到了防抖(你慢慢抖吧,等你抖完了我再给请你吃 kfc)
同样的指令接收一个 arg 和 value,直接看代码实现
<template>
<input v-debounceInput:500="toKfc" />
</template>
<script setup>
const vDebounceInput = (el, bind) => {
if (typeof bind.value !== "function") return;
bind.value.id = null;
el.addEventListener("input", () => {
clearTimeout(bind.value.id);
bind.value.id = setTimeout(() => {
bind.value();
}, bind.arg || 1000);
});
};
const toKfc = () => {
console.log("优弧请我吃kfc");
};
</script>
然后我们看下效果
全局注册指令
如果我们想要在每个组件都能使用这两个指令,我们可以将其注册到全局。我们可以在 main.js 中进行注册,以 debounceInput 为例
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
const app = createApp(App);
app.directive("debounceInput", (el, bind) => {
if (typeof bind.value !== "function") return;
bind.value.id = null;
el.addEventListener("input", () => {
clearTimeout(bind.value.id);
bind.value.id = setTimeout(() => {
bind.value();
}, bind.arg || 1000);
});
});
app.mount("#app");
写在最后
vue 的自定义指令在平时工作中用到的地方还是很多的,比如权限管理,事件埋点等等都可以使用指令实现,非常的方便,所以对 vue 自定义指令还不熟悉的小伙伴可以直接到官网查看文档 呦