vue2中常用的自定义指令,可以用于扩展 Vue 的功能,处理一些特定的 DOM 操作或交互需求,如有需要请点击进行查看自定义指令参数详解
存放自定义指令文件目录
以下为全局自定义指令封装目录,在src目录下建一个专门存储指令的文件夹directive,directive文件夹下新建modules模块文件夹,再建一个和modules平级的index.js文件。目录如下:
// 指令文件目录
--src
|---directive
|-----modules
|-------focus.js // 自动聚焦指令
|-------highlight.js // 文本高亮指令
|-----index.js
|---main.js // vue项目的入口文件
directive下的index.js文件内容
// 注册全局指令
import auth from "./modules/auth"
import focus from "./modules/focus"
const directives = {
auth,
focus,
}
export default {
install(Vue) {
Object.keys(directives).forEach((key) => {
Vue.directive(key, directives[key])
})
},
}
main.js文件引入注册的指令
import Vue from "vue";
import App from "./App";
import router from "./router";
import store from "./store";
// 引入指令
import Directive from "@/directive";
Vue.use(Directive)
new Vue({
el: '#app',
router,
store,
components: {App},
template: '<App/>'
});
1. v-focus:自动聚焦指令
用途: 需要页面加载或特定交互后自动聚焦到输入框的场景,比如:登录页,表单弹窗,搜索框(页面刷新后直接聚焦搜索框)。 案例代码:
// 焦点指令
const focus = {
inserted(el) {
// el:指令所绑定的元素,可以用来直接操作DOM
// 此处使用的是el-input组件,所以使用el-input__inner来获取焦点,el-input__inner元素是el-input的子元素
const input = el.querySelector('.el-input__inner');
if (input) {
input.focus();
}
}
}
export default focus
使用:
<el-input v-focus v-model="focusValue" placeholder="鼠标自动聚焦"></el-input>
详解:
inserted钩子在元素插入到 DOM 时触发,调用input.focus()使元素自动聚焦。
注意事项:
- 如果是其他组件库,查看
el下标签input元素的类名替换.el-input__inner即可。
2. v-copy:复制指令
用途: 需要一键复制文本内容的场景,比如:订单号复制,分享链接,代码片段等。 案例代码:
const copy = {
bind: function (el, binding) {
el.addEventListener('click', async () => {
console.log('复制文本:', binding.value,navigator.clipboard)
const text = binding.value?.text || binding.value; // 兼容对象或直接文本
try {
// 优先使用现代 Clipboard API
if (navigator.clipboard) {
await navigator.clipboard.writeText(text);
} else {
// 兼容旧版 execCommand
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed'; // 避免滚动到底部
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
// 成功回调(可选)可替换成成功弹出提示框
binding.value?.success?.() || console.log('复制成功:', text);
} catch (err) {
// 失败回调(可选)
binding.value?.error?.(err) || console.error('复制失败:', err);
}
});
// 添加光标样式提示
el.style.cursor = 'pointer';
}
}
export default copy
使用:
示例 1:直接复制固定文本
<button v-copy="'https://example.com'">复制链接</button>
示例 2:复制动态数据
<template>
<div>
<input v-model="copyText" />
<button v-copy="copyText">复制输入框内容</button>
</div>
</template>
<script>
export default {
data() {
return { copyText: '默认文本' };
}
}
</script>
详解:
- 现代浏览器:优先使用
navigator.clipboard.writeText(),需要 HTTPS 环境。 - 旧版浏览器:通过
document.execCommand('copy')兼容:- 创建隐藏的
textarea - 选中文本并执行复制
- 移除临时元素
- 创建隐藏的
3. v-auth:权限控制指令
用途: 根据用户权限动态显示或禁用按钮,比如:仅管理员可见的“删除”或“编辑”按钮,仅管理员可见的“删除”或“编辑”按钮。 案例代码:
// auth.js
/**
* 权限指令模块,用于根据用户权限动态控制DOM元素的显示与隐藏。
* 该模块通过检查用户的权限列表来决定是否移除或替换指定的DOM元素。
*/
import store from '@/store'; // 导入获取本地存储权限的工具函数
// 权限指令
const auth = {
inserted(el, { value }) {
if (!value) {
return;
}
// 获取用户权限数组(这里以vuex为案例,需根据项目实际情况调整)
const permissions = store.getters.authList || [];
if (!(permissions.includes(value) || permissions.includes("*:*:*"))) {
// 如果没有权限且权限值包含"edit",则进行编辑权限单独处理
if (value.includes("edit")) {
handleEditPermission(el);
} else {
// 如果没有权限且权限值不包含"edit",则移除元素
el.remove();
}
}
}
}
/**
* 处理编辑权限的特殊逻辑。
* 将原元素替换为一个新创建的span元素,并保留原文本内容。
* @param {HTMLElement} el - 需要处理的DOM元素
*/
function handleEditPermission(el) {
const parentNode = el.parentNode;
if (parentNode) {
const newNode = document.createElement("span"); // 创建新的span元素
newNode.textContent = el.textContent; // 设置新元素的文本内容为原元素的文本内容
parentNode.appendChild(newNode); // 将新元素插入到父节点中
el.remove(); // 移除原元素
}
}
export default auth; // 导出权限指令对象
使用:
<button v-auth="'ad:sys:add'">新增</button>
<!-- 此编辑按钮是模拟在点击表格中的单元格,点击系统名称,打开编辑信息,如果没有权限是要让此点击事件失效,故在权限指令处对编辑权限进行了单独处理 -->
<el-table-column label="系统名称" width="180">
<template slot-scope="scope">
<div v-auth="'ad:sys:edit'" @click="handleClick(row)">{{ scope.row.name }}</div>
</template>
</el-table-column>
详解:
inserted钩子在元素插入时触发,根据权限值决定是否隐藏或者移除元素。
4. v-longpress:长按指令
用途: 常用于移动端或需要长按交互的场景(如游戏、编辑器等)。 案例代码:
const longpress = {
bind: function(el, binding) {
// 定义长按触发的回调函数
const handler = binding.value;
// 定义计时器变量
let pressTimer = null;
// 开始计时函数
const start = (e) => {
// 阻止默认行为(如移动端长按菜单)
e.preventDefault();
// 确保只有一个计时器运行
if (pressTimer === null) {
pressTimer = setTimeout(() => {
// 执行长按回调,并传递原生事件对象
handler(e);
}, binding.arg || 800); // 默认800ms,可通过指令参数修改
}
};
// 取消计时函数
const cancel = () => {
if (pressTimer !== null) {
clearTimeout(pressTimer);
pressTimer = null;
}
};
// 存储事件处理函数,便于解绑时使用
el._longPressHandlers = { start, cancel };
// 绑定事件监听
// 桌面端:mousedown(按下)和 mouseup(松开)
// 移动端:touchstart(触摸开始)和 touchend(触摸结束)
// 额外监听 click 和 touchcancel 确保意外操作时取消计时器。
el.addEventListener('mousedown', start);
el.addEventListener('touchstart', start);
el.addEventListener('click', cancel);
el.addEventListener('mouseup', cancel);
el.addEventListener('touchend', cancel);
el.addEventListener('touchcancel', cancel);
},
unbind: function(el) {
// 解绑所有事件监听
const { start, cancel } = el._longPressHandlers || {};
if (start) {
el.removeEventListener('mousedown', start);
el.removeEventListener('touchstart', start);
}
if (cancel) {
el.removeEventListener('click', cancel);
el.removeEventListener('mouseup', cancel);
el.removeEventListener('touchend', cancel);
el.removeEventListener('touchcancel', cancel);
}
}
};
export default longpress;
使用: 示例 1:基础用法,默认时长
<template>
<button v-longpress="handleLongPress">长按我</button>
</template>
<script>
export default {
methods: {
handleLongPress() {
console.log('长按成功!');
}
}
}
</script>
示例 2:自定义时长时长
<template>
<button v-longpress:1500="handleLongPress">1.5秒后触发</button>
</template>
<script>
export default {
methods: {
handleLongPress() {
console.log('长按成功!');
}
}
}
</script>
详解:
-
监听按下事件:通过
mousedown(桌面端)和touchstart(移动端)触发长按计时。 -
设置计时器:按下后启动计时器,若在指定时间内未松开,则触发长按回调。
-
取消机制:通过
mouseup、touchend等事件取消计时器,避免误触发。 -
清理资源:在指令解绑时移除事件监听,防止内存泄漏。
5. v-draggable:元素拖拽效果指令
用途: 需要用户手动调整元素位置的场景,比如:网页客服icon显示位置。 案例代码:
const draggable = {
inserted: function (el, binding) {
// 初始化变量
let isDragging = false; // 标记是否正在拖拽
let initialX = 0; // 拖拽开始时的初始X坐标
let initialY = 0; // 拖拽开始时的初始Y坐标
let currentX = 0; // 当前的X坐标
let currentY = 0; // 当前的Y坐标
// 处理桌面端事件
const onMouseDown = (e) => {
isDragging = true;
initialX = e.clientX - currentX;
initialY = e.clientY - currentY;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
e.preventDefault(); // 阻止默认选中文本行为
};
// PC端移动事件
const onMouseMove = (e) => {
if (!isDragging) return;
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
updatePosition();
};
// 停止拖拽
const endDrag = () => {
isDragging = false;
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
// 更新元素位置
const updatePosition = () => {
// 使用 transform 优化性能(避免重排)
// “移动重排”通常指的是在重新布置元素时动态改变元素的位置或布局,这种操作会影响整个页面或部分页面的布局,导致浏览器重新计算和渲染布局。
el.style.transform = `translate(${currentX}px, ${currentY}px)`;
el.style.zIndex = 9999;
// 若需保留原始定位,可使用以下方式:
// el.style.left = currentX + 'px';
// el.style.top = currentY + 'px';
};
// 鼠标释放事件
const onMouseUp = (e) => {
endDrag();
};
// 绑定事件
el.addEventListener('mousedown', onMouseDown);
// 清理函数
el._cleanupDraggable = () => {
el.removeEventListener('mousedown', onMouseDown);
endDrag();
};
// 初始化元素样式(确保可拖拽)
el.style.position = 'relative'; // 或 'absolute'/'fixed'
el.style.userSelect = 'none'; // 阻止拖拽时选中文本
},
unbind: function (el) {
// 解绑所有事件
el._cleanupDraggable?.();
}
}
export default draggable
使用:
<template>
<div v-draggable class="box">拖拽我</div>
</template>
<style>
.box {
width: 100px;
height: 100px;
background: #42b983;
cursor: move;
}
</style>
详解:
- 事件监听:通过 mousedown)触发拖拽。
- 坐标计算:记录初始点击位置与元素位置的偏移量。
- 动态更新位置:在 mousemove 事件中实时更新元素位置。
- 释放资源:在 mouseup 事件中移除移动事件监听。
- 边界控制(可选):限制元素在父容器或可视区域内移动。
6. v-debounce:防抖指令
用途: 防止按钮在短时间内被多次点击,使用防抖函数限制规定时间内只能点击一次,比如:输入事件,提交事件等。 案例代码:
const debounce = {
// 防抖指令:v-debounce
bind(el, binding) {
// 解析参数:格式为 "事件类型.延迟时间",如 "input.300"
const [event, delay] = binding.arg ?
binding.arg.split('.') :
['input', 500]; // 默认事件 input,延迟 500ms
// 防抖时间转为数字
const debounceTime = parseInt(delay) || 500;
// 保存原始事件处理函数
const handler = binding.value;
// 防抖函数容器
let debounceTimer = null;
// 创建防抖后的处理函数
const debouncedHandler = (e) => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
handler(e); // 传递事件对象 e 给用户函数
}, debounceTime);
};
// 绑定事件监听
el.addEventListener(event, debouncedHandler);
// 在元素上保存引用,用于解绑
el._debounce = {event, handler: debouncedHandler};
},
unbind(el) {
// 移除事件监听
if (el._debounce) {
const {event, handler} = el._debounce;
el.removeEventListener(event, handler);
el._debounce = null;
}
}
}
export default debounce;
使用: 示例 1:基础用法,输入框防抖
<<template>
<input
v-debounce:input.300="handleInput"
placeholder="输入内容后 300ms 触发"
/>
</template>
<script>
export default {
methods: {
handleInput(e) {
console.log('防抖后的值:', e.target.value);
}
}
}
</script>
示例 2: 按钮点击防抖
<template>
<button v-debounce:click.1000="handleClick">点击后 1 秒内只能触发一次</button>
<button v-debounce:click.1000="()=>handleClickParam(params))">携带参数的函数指令调用</button>
</template>
<script>
export default {
data(){
return{
params:"这是参数"
}
}
methods: {
handleClick() {
console.log('按钮点击生效!');
},
handleClickParam(value) {
console.log('按钮点击生效!',value);
},
}
}
</script>
详解:
- 提取指令参数:
binding.arg获取指令参数(如 input.300) - 定义计时器:
debounceTimer用于存储setTimeout的引用 - 清除旧计时器:每次事件触发时,先清除之前的计时器(避免重复执行)
- 设置新计时器:延迟
debounceTime后执行用户回调函数 handler(e) - 传递事件对象:将原生事件 e 传递给用户函数,便于获取事件目标(如 e.target.value)