vue3 自定指令防抖/节流/去空格建

439 阅读2分钟

vue3 自定指令防抖/节流/去空格建

防抖

/**
 * v-debounce
 * 按钮防抖指令
 * 防抖就是在行为触发执行一次后,需静默等待N秒后才能再次执行,在此期间不能再触发行为,如果重复触发就计时归零后再次计时,直到用户静默到设定的阈值才再次执行。
 * 简而言之就是触发后N秒内不许触发,直到N秒后才可以执行。
 * <el-button type="primary" icon="search" plain v-debounce:500="handleSearch">搜索</el-button>
 */
// @ts-nocheck
import type { Directive, DirectiveBinding } from "vue";
interface ElType extends HTMLElement {
  handleClick: () => any;
}
const debounce: Directive = {
  mounted(el: ElType, binding: DirectiveBinding) {
    if (typeof binding.value !== "function") {
      throw "callback must be a function";
    }
    let timer = null;
    const delay = parseInt(binding.arg) || 500;
    const handler = binding.value;

    el.addEventListener(
      "click",
      () => {
        clearTimeout(timer);
        timer = setTimeout(() => {
          handler();
        }, delay);
      },
      { passive: false }
    );
  },
};

export default debounce;

节流

/*
  需求:防止按钮在短时间内被多次点击,使用节流函数限制规定时间内只能点击一次。
  节流就是N秒内只执行一次,不管在N秒内重复调用了多少次,也只执行一次。
  思路:
    1、第一次点击,立即调用方法并禁用按钮,等延迟结束再次激活按钮
    2、将需要触发的方法绑定在指令上
  
  使用:给 Dom 加上 v-throttle 及回调函数即可
  <el-button type="danger" icon="refresh" plain v-throttle="resetSearch">重置</el-button>
*/
// @ts-nocheck
import type { Directive, DirectiveBinding } from "vue";
interface ElType extends HTMLElement {
  handleClick: () => any;
  disabled: boolean;
}
const throttle: Directive = {
  mounted(el: ElType, binding: DirectiveBinding) {
    let timer = null;
    const delay = parseInt(binding.arg) || 500;
    const handler = binding.value;
    let lastExecTime = 0;

    el.addEventListener(
      "click",
      () => {
        const now = Date.now();
        if (now - lastExecTime > delay) {
          handler();
          lastExecTime = now;
        }
      },
      { passive: false }
    );
  },
};

export default throttle;

去input空格

/**
 * 去除两边空格
 * <el-input v-model="xxx" v-trim></el-input>
 */

function getInput(el: any) {
  let inputEle;
  const { tagName } = el;
  if (tagName === "INPUT" || tagName === "TEXTAREA") {
    inputEle = el;
  } else {
    inputEle = el.querySelector("input");
    if (!inputEle) {
      inputEle = el.querySelector("textarea");
    }
  }
  return inputEle;
}

function dispatchEvent(el: any, type: any) {
  let evt = document.createEvent("HTMLEvents");
  evt.initEvent(type, true, true);
  el.dispatchEvent(evt);
}

const Trim: any = {
  mounted: (el: any) => {
    if (!el) return;
    let inputEle = getInput(el);
    const handler = function (event: any) {
      const newVal = event.target.value.trim();
      if (event.target.value != newVal) {
        event.target.value = newVal;
        dispatchEvent(inputEle, "input");
      }
    };
    el.inputEle = inputEle;
    el._blurHandler = handler;
    inputEle?.addEventListener("blur", handler);
  },

  beforeUnmount(el: any) {
    const { inputEle } = el;
    inputEle?.removeEventListener("blur", el._blurHandler);
  },
};

Trim.install = (app: any) => {
  app.directive("trim", Trim);
};

export default Trim;

注册

import { App } from "vue";
import trim from "./trim/index";
import debounce from "./debounce/index";
import throttle from "./throttle/index";

const directivesList: any = {
  trim,
  debounce,
  throttle,
};

const directives = {
  install: function (app: App<Element>) {
    Object.keys(directivesList).forEach((key) => {
      // 注册所有自定义指令
      app.directive(key, directivesList[key]);
    });
  },
};

export default directives;

引入main.ts

import directives from '@/directives/index';
import directives from '@/directives/index';

结束!!!