在实际开发中,很多重复逻辑(权限控制、防抖点击、图片懒加载、文本复制等)用自定义指令来做最优雅,不污染组件、不写冗余代码、复用性极强。
今天整理了 10 个企业级最常用的 Vue 自定义指令,Vue2 / Vue3 都能跑,复制到项目里直接用,建议收藏进你的工具库。
1. v-permission 按钮权限控制(后台系统必用)
根据权限码控制按钮显隐,后端返回权限列表直接用。
js
// directives/permission.js
import { useUserStore } from '@/stores/user'
export default {
mounted(el, binding) {
const { permissions } = useUserStore()
const value = binding.value
if (!value) return
// 无权限则移除元素
if (!permissions.includes(value)) {
el.parentNode?.removeChild(el)
}
}
}
使用:
html
<button v-permission="'user:add'">添加用户</button>
2. v-debounce 防抖点击(搜索/提交防重复)
js
// directives/debounce.js
export default {
mounted(el, binding) {
const { func, delay = 300 } = binding.value
let timer = null
el.addEventListener('click', () => {
clearTimeout(timer)
timer = setTimeout(() => func(), delay)
})
}
}
使用:
html
<button v-debounce="{ func: handleSearch, delay: 500 }">搜索</button>
3. v-throttle 节流指令(滚动/防狂点)
js
// directives/throttle.js
export default {
mounted(el, binding) {
const { func, delay = 500 } = binding.value
let lastTime = 0
el.addEventListener('click', () => {
const now = Date.now()
if (now - lastTime >= delay) {
func()
lastTime = now
}
})
}
}
4. v-copy 一键复制文本
js
// directives/copy.js
export default {
mounted(el, binding) {
el.addEventListener('click', () => {
const text = binding.value
navigator.clipboard.writeText(text).then(() => {
ElMessage.success('复制成功')
})
})
}
}
使用:
html
<span v-copy="orderNo">复制订单号</span>
5. v-longpress 长按指令
js
// directives/longpress.js
export default {
mounted(el, binding) {
const { func, time = 1000 } = binding.value
let timer = null
el.addEventListener('mousedown', () => {
timer = setTimeout(() => func(), time)
})
el.addEventListener('mouseup mouseleave', () => clearTimeout(timer))
}
}
6. v-input-number 仅允许输入数字(支持小数)
js
// directives/number.js
export default {
mounted(el) {
const input = el.tagName === 'INPUT' ? el : el.querySelector('input')
input.addEventListener('input', () => {
input.value = input.value.replace(/[^\d.]/g, '')
const arr = input.value.split('.')
if (arr.length > 2) input.value = arr[0] + '.' + arr[1]
})
}
}
使用:
html
<el-input v-input-number v-model="num" />
7. v-lazy 图片懒加载(性能优化)
js
// directives/lazy.js
export default {
mounted(el, binding) {
const observer = new IntersectionObserver(([{ isIntersecting }]) => {
if (isIntersecting) {
el.src = binding.value
observer.unobserve(el)
}
})
observer.observe(el)
}
}
使用:
html
<img v-lazy="imgUrl" alt="" />
8. v-draggable 元素拖拽
js
// directives/drag.js
export default {
mounted(el) {
el.style.cssText += ';position:fixed;cursor:move;'
el.addEventListener('mousedown', (e) => {
const x = e.clientX - el.offsetLeft
const y = e.clientY - el.offsetTop
const move = (e) => {
el.style.left = e.clientX - x + 'px'
el.style.top = e.clientY - y + 'px'
}
document.addEventListener('mousemove', move)
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', move)
}, { once: true })
})
}
}
9. v-watermark 页面水印(防截图)
js
// directives/watermark.js
export default {
mounted(el, binding) {
const text = binding.value || '内部资料'
const canvas = document.createElement('canvas')
canvas.width = 200
canvas.height = 150
const ctx = canvas.getContext('2d')
ctx.font = '14px Arial'
ctx.fillStyle = 'rgba(0,0,0,0.1)'
ctx.rotate(-0.2)
ctx.fillText(text, 20, 50)
el.style.background = `url(${canvas.toDataURL()}) repeat`
}
}
10. v-auto-height 自适应高度(表格/弹窗常用)
自动计算高度,避免滚动条错乱
js
// directives/autoHeight.js
export default {
mounted(el) {
const resize = () => {
const top = el.getBoundingClientRect().top
el.style.height = window.innerHeight - top - 20 + 'px'
}
resize()
window.addEventListener('resize', resize)
el._resize = resize
},
unmounted(el) {
window.removeEventListener('resize', el._resize)
}
}
统一注册(Vue3)
在 directives/index.js 统一导出:
js
import permission from './permission'
import debounce from './debounce'
// ...其他
export default {
install(app) {
app.directive('permission', permission)
app.directive('debounce', debounce)
}
}
main.js 引入:
js
import directives from '@/directives'
app.use(directives)
各位互联网搭子,要是这篇文章成功引起了你的注意,别犹豫,关注、点赞、评论、分享走一波,让我们把这份默契延续下去,一起在知识的海洋里乘风破浪!