Vue3 + TypeScript 自定义指令实践

500 阅读2分钟

准备工作

在开始编写自定义指令之前,请确保你已经安装了Vue3和TypeScript,并且能够创建Vue项目。

自定义指令示例

复制指令

复制指令用于实现点击元素后将其内容复制到剪贴板的功能。

/**
 * v-copy
 * 复制某个值至剪贴板
 * 接收参数:string类型/Ref<string>类型/Reactive<string>类型
 */
import type { Directive, DirectiveBinding } from "vue";
import { ElMessage } from "element-plus";
interface ElType extends HTMLElement {
	copyData: string | number;
	__handleClick__: any;
}
const copy: Directive = {
	mounted(el: ElType, binding: DirectiveBinding) {
		el.copyData = binding.value;
		el.addEventListener("click", handleClick);
	},
	updated(el: ElType, binding: DirectiveBinding) {
		el.copyData = binding.value;
	},
	beforeUnmount(el: ElType) {
		el.removeEventListener("click", el.__handleClick__);
	}
};

async function handleClick(this: any) {
	if (!navigator.clipboard) {
		const input = document.createElement("input");
		input.value = this.copyData.toLocaleString();
		document.body.appendChild(input);
		input.select();
		document.execCommand("Copy");
		document.body.removeChild(input);
	} else {
		await navigator.clipboard.writeText(this.copyData);
	}
	ElMessage({
		type: "success",
		message: "复制成功"
	});
}

export default copy;

防抖指令

防抖指令用于延迟执行某个函数,常用于输入框搜索等场景。

/**
 * v-debounce
 * 按钮防抖指令,可自行扩展至input
 * 接收参数:function类型
 */
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: NodeJS.Timeout | null = null;
		el.__handleClick__ = function () {
			if (timer) {
				clearInterval(timer);
			}
			timer = setTimeout(() => {
				binding.value();
			}, 500);
		};
		el.addEventListener("click", el.__handleClick__);
	},
	beforeUnmount(el: ElType) {
		el.removeEventListener("click", el.__handleClick__);
	}
};

export default debounce;

节流指令

节流指令用于限制函数的执行频率,常用于滚动加载等场景。

/*
  需求:防止按钮在短时间内被多次点击,使用节流函数限制规定时间内只能点击一次。

  思路:
    1、第一次点击,立即调用方法并禁用按钮,等延迟结束再次激活按钮
    2、将需要触发的方法绑定在指令上
  
  使用:给 Dom 加上 v-throttle 及回调函数即可
  <button v-throttle="debounceClick">节流提交</button>
*/
import type { Directive, DirectiveBinding } from "vue";
interface ElType extends HTMLElement {
	__handleClick__: () => any;
	disabled: boolean;
}
const throttle: Directive = {
	mounted(el: ElType, binding: DirectiveBinding) {
		if (typeof binding.value !== "function") {
			throw "callback must be a function";
		}
		let timer: NodeJS.Timeout | null = null;
		el.__handleClick__ = function () {
			if (timer) {
				clearTimeout(timer);
			}
			if (!el.disabled) {
				el.disabled = true;
				binding.value();
				timer = setTimeout(() => {
					el.disabled = false;
				}, 1000);
			}
		};
		el.addEventListener("click", el.__handleClick__);
	},
	beforeUnmount(el: ElType) {
		el.removeEventListener("click", el.__handleClick__);
	}
};

export default throttle;

注册自定义指令

在Vue应用的入口文件中,注册自定义指令以便在整个应用中使用。

// directives/index.ts
import { App } from "vue";
import copy from "./modules/copy";
import debounce from "./modules/debounce";
import throttle from "./modules/throttle";

const directivesList: any = {
	copy,
	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 { createApp } from "vue";
import App from "./App.vue";
import directives from "@/directives/index";

const app = createApp(App);
app.use(directives).mount("#app");

总结

通过本文的介绍,我们学习了如何结合Vue3和TypeScript编写自定义指令,并实现了复制、防抖、节流等几种常见的自定义指令。自定义指令的使用能够让我们更好地控制DOM元素的行为,提高开发效率,降低代码重复度。在实际项目中,可以根据需求编写更多更复杂的自定义指令,以满足不同的业务场景。

希望本文能够帮助到你,欢迎探索更多关于Vue3和TypeScript的技术内容!