想学Vue3自定义指令却不知道从哪入手? - Web前端
作者:98wpeu发布时间:2026-10-22分类:网页前端技术浏览:14

你可以把自定义指令理解成“强化版DOM操作工具”,Vue组件是封装UI和逻辑,但自定义指令更聚焦dom行为的复用——比如让输入框自动聚焦、根据权限隐藏按钮、输入时自动格式化内容…这些和DOM强相关的逻辑,用指令封装后,在模板里加个v-xxx就能复用,比在组件里写重复代码清爽多了。
举个直观例子:登录页有个输入框,每次进入页面要自动聚焦,如果不用指令,得在组件onMounted里写inputref.value.focus();但用自定义指令v-focus,只需要在输入框上加v-focus,所有需要自动聚焦的输入框都能复用这个逻辑,不用在每个组件里重复写DOM操作。
怎么创建第一个自定义指令?
分全局指令和局部指令两种注册方式,先从最简单的“自动聚焦”例子入手:
全局指令(全项目可用)
在vue3的入口文件(比如mAIn.JS)里,用APP.directive注册:
import { createapp } from 'vue'
import App from './app.vue'
const app = createApp(App)
// 注册全局指令v-focus
app.directive('focus', {
// 钩子函数:DOM挂载后执行(此时才能操作DOM)
mounted(el) {
el.focus() // el是指令绑定的DOM元素
}
})
app.mount('#app')然后在任意组件的模板里用:
<template> <input v-focus placeholder="自动聚焦到我~" /> </template>
局部指令(只在当前组件可用)
如果指令只用在某个组件,就在组件的directives选项里定义:
<template>
<input v-focus Placeholder="局部指令的自动聚焦" />
</template>
<script setup>
// 局部注册v-focus
const directives = {
focus: {
mounted(el) {
el.focus()
}
}
}
</script>自定义指令的钩子函数有啥讲究?
Vue3给指令设计了7个钩子函数,对应DOM不同生命周期,理解它们的执行时机是写好指令的关键:
| 钩子函数 | 执行时机 | 常用场景 |
|---|---|---|
created | 指令绑定元素的js对象创建后(DOM还没生成) | 少用,此时拿不到DOM |
beforemount | 指令绑定元素插入DOM前 | 提前准备数据(用得少) |
mounted | 指令绑定元素插入DOM后 | 操作DOM(比如focus、加事件监听) |
beforeUpdate | 组件更新前(数据变了,DOM还没更) | 提前处理数据(用得少) |
upDated | 组件更新后(数据和DOM都更新了) | 基于新DOM做操作(比如重新计算高度) |
beforeUnmount | 组件卸载前 | 准备清理工作(比如移除定时器) |
unmounted | 组件卸载后 | 彻底清理(比如移除事件监听) |
举个实战例子:做一个“点击outside隐藏弹窗”的指令v-click-outside,需要在mounted时给文档加点击事件监听,在unmounted时移除监听(否则会内存泄漏):
app.directive('click-outside', {
mounted(el, binding) {
// binding.value是指令绑定的函数,比如v-click-outside="closeDialog"
const handleClick = (e) => {
if (!el.contains(e.target)) {
binding.value() // 执行关闭弹窗的函数
}
}
document.addEventListener('click', handleClick)
// 把事件存到el上,方便unmounted时移除
el._clickOutsideHandler = handleClick
},
unmounted(el) {
document.removeeventlistener('click', el._clickOutsideHandler)
}
})实战场景1:权限控制指令v-permission
项目里常见需求:不同角色看到的按钮不一样(删除”按钮只有管理员能看到),用v-permission指令实现根据权限动态控制DOM显示:
需求分析
指令接收“权限标识数组”,比如
v-permission="['admin', 'editor']"页面加载时,检查用户是否有至少一个权限,没有就隐藏/删除按钮
实现步骤
假设用户权限存在Pinia或vuex里,比如
useUserstore().roles是当前用户角色数组。注册全局指令
v-permission:import { useUserStore } from './stores/user'
app.directive('permission', { mounted(el, binding) { const userStore = useUserStore() const requiredRoles = binding.value // 指令绑定的权限数组,'admin'] // 检查用户是否有任一权限 const hasPermission = requiredRoles.some(role => userStore.roles.includes(role)) if (!hasPermission) { // 两种隐藏方式:移除DOM(彻底消失)或设为display:none(保留位置) el.parentNode?.removeChild(el) // 或者 el.style.display = 'none' } } })
3. 模板中使用: ```VUE <template> <!-- 只有admin能看到这个按钮 --> <button v-permission="['admin']">删除文章</button> </template>
实战场景2:防抖指令v-debounce
搜索框输入时,每次输入都发请求会浪费性能,用v-debounce让请求延迟触发(比如输入停止500ms后再发请求):
需求分析
指令绑定在输入框,监听
input事件每次输入时清除之前的定时器,重新计时,延迟执行请求函数
实现步骤
注册全局指令
v-debounce,处理定时器逻辑:app.directive('debounce', { mounted(el, binding) { let timer = null const delay = 500 // 防抖延迟,也可以让指令接收参数控制 el.addEventListener('input', () => { clearTimeout(timer) timer = setTimeout(() => { binding.value() // 执行指令绑定的函数,比如handleSearch }, delay) }) } })组件中使用(假设
handleSearch是发请求的函数):<template> <input v-model="searchKey" v-debounce="handleSearch" placeholder="搜索..." /> </template>
```
实战场景3:输入格式限制指令v-FORMat
比如手机号输入时自动变成“3-4-4”格式(138 1234 5678),或只能输入数字,用v-format监听输入,实时格式化内容:
需求分析(以手机号为例)
输入时,把连续数字按
/(\d{3})(\d{4})(\d{4})/分割,加空格同时要更新
v-model绑定的值(因为输入框的v-model是双向绑定,指令要触发input事件让Vue感知变化)
实现步骤
注册全局指令
v-Format:app.directive('format', { mounted(el) { el.addEventListener('input', () => { // 去除所有非数字字符 let value = el.value.Replace(/\D/g, '') // 格式化为3-4-4 if (value.length > 3) { value = `${value.slice(0, 3)} ${value.slice(3, 7)} ${value.slice(7)}` } else if (value.length > 0) { value = value.slice(0, 3) } el.value = value // 触发input事件,让v-model更新绑定值 el.dispatchEvent(new Event('input')) }) } })模板中使用:
<template> <input v-model="phone" v-format placeholder="请输入手机号" /> </template>
```
实战场景4:加载状态指令v-loading
按钮点击后显示“加载中”,防止重复点击,用v-loading控制按钮状态和样式:
需求分析
指令接收一个布尔值,控制是否显示加载
点击按钮时,若处于加载中则禁用按钮,显示加载文字
实现步骤
注册全局指令
v-loading,结合样式和状态控制:app.directive('loading', { mounted(el, binding) { // 初始化:如果绑定值为true,直接设置加载状态 if (binding.value) { el.disabled = true el.innerHTML = '加载中...' } // 监听值变化(用updated钩子,因为mounted只执行一次) }, updated(el, binding) { if (binding.value) { el.disabled = true el.innerhtml = '加载中...' } else { el.disabled = false // 恢复原文字(假设按钮原文字存在data-original里) el.innerHTML = el.dataset.original || '提交' } } })组件中使用(结合异步函数):
<template> <button v-loading="isLoading" data-original="提交" @click="handleSubmit" >提交</button> </template>
```
自定义指令的进阶技巧:传参和修饰符
指令也能像组件一样传参数和加修饰符,让逻辑更灵活,比如v-permission:admin.lazy="true",
:admin是参数(通过binding.arg获取).lazy是修饰符(通过binding.modifiers.lazy获取)
例子:带参数的权限指令
需求:区分“查看”和“编辑”权限,用参数arg控制:
app.directive('permission', {
mounted(el, binding) {
const action = binding.arg // quot;vIEw"或"edit"
const requiredRoles = binding.value
// 假设权限格式是 { view: ['admin'], edit: ['editor'] }
const userStore = useUserStore()
const hasPermission = requiredRoles.some(role => userStore.roles[action].includes(role))
if (!hasPermission) {
el.parentnode?.removeChild(el)
}
}
})模板中使用:
<!-- 只有admin能查看,editor能编辑 --> <button v-permission:view="['admin']">查看</button> <button v-permission:edit="['editor']">编辑</button>
写自定义指令要避开哪些坑?
内存泄漏:给DOM加了事件监听(比如
addEventListener),一定要在unmounted钩子用removeEventListener移除,否则组件销毁后事件还在,会触发错误。DOM结构影响:用
el.parentNode.removeChild(el)删除元素时,要考虑父组件布局会不会乱(比如按钮列表少了一个元素),如果只是“隐藏”,用el.style.display = 'none'更安全。响应式数据更新:如果指令依赖的变量(比如用户权限、加载状态)变化了,要在
updated钩子处理,因为mounted只执行一次,变量变化后不会重新触发。指令复用冲突:如果多个组件用同一个指令,要确保指令内部状态不共享(比如定时器ID、事件处理函数),可以把状态存在
el的自定义属性里(比如el._timer),避免不同元素互相影响。
自定义指令是Vue3里很“接地气”的工具——把重复的DOM操作逻辑封装成v-xxx,既让模板干净,又能复用,从自动聚焦、权限控制到防抖、输入格式化,这些场景练一遍,遇到类似需求直接套思路,多在项目里试几个指令,手感自然就来了~
相关推荐
- 有用的内容更新后,关键字优化如何变化? - Web前端
- 页面内存泄漏怎么检测?手把手教你定位和解决 - Web前端
- React Router DOM如何传递和获取路由参数? - Web前端
- React Router DOM如何结合History对象管理导航? - Web前端
- JavaScript原型链是怎么工作的? - Web前端
- 前端开发:语法结构,写代码的「简洁度」差异 - Web前端
- JavaScript里var、let、const声明变量有啥区别? - Web前端
- 先搞懂同步与异步,Node.js 文件操作的两种逻辑 - Web前端
- 想在Node.js里给接口发请求、抓网页数据,却不知道从哪下手? - Web前端
- JavaScript 怎么检测数据类型?常用方法全解析 - Web前端
- 网页前端技术排行
-
- 1[Web前端]用javascript实现默认图片替代未显示的图片 - Web前端
- 2分析Iconfont-阿里巴巴矢量常用图标库 - Web前端
- 3jQuery实例教程:制作网页中可折叠的面板 - Web前端
- 4【第六章】Foundation之按钮和下拉功能 - Web前端
- 5基于jquery的滚动条滚动固定div(附演示下载) - Web前端
- 6分享精心挑选的12款优秀jQuery Ajax分页插件和教程 - Web前端
- 7jQuery实现鼠标移到元素上动态提示消息框效果 - Web前端
- 8jQuery编写widget的一些技巧分享 - Web前端
- 9在Mac/PC上远程调试iPhone/iPad上的网页 - Web前端
- 最近发表
-
- WordPress随机显示特色图片插件:Random Post Thumbnails
- KeePass实现Chrome浏览器自动填充密码方法一
- LNMP一键包nginx 301强制跳转到https教程
- KeePass实现Chrome浏览器自动填充密码方法二
- #建站# 免费的VPS管理软件Xshell8/Xftp8中文版下载
- 使用Xshell 8连接VPS教程_电脑登录vps的方法
- WordPress评论界面添加烟花????效果
- 不同浏览器书签同步方案:坚果云+Floccus_详细使用教程
- iOS端KeePassXC客户端APP:Strongbox Password Safe
- 给WordPress评论中的Gravatar头像图片添加ALT属性


