介绍
自定义指令在Vue2和Vue3中都是一个很棒的特性
# 什么是自定义全局指令
自定义全局指令 (opens new window), 在Vue的官方对自定义指令进行了完善的说明, 简单来说自定义指令是 Vue 内置以外不支持的指令, 并且这些指令依赖Dom节点, 在这种情况下, 开发者自己定义的指令
- 所有的自定义指令的名称都为
v-指令名称
,Vue会自动给你加上v
- 自定义指令内部也有一套生命周期(钩子函数)
- 自定义指令分支持简写(自动执行生命周期中的
mounted
和updated
)
提示
所需功能只能通过直接的 DOM 操作来实现时,才应该使用自定义指令。其他情况下应该尽可能地使用 v-bind @
这样的内置指令来声明式地使用模板,这样更高效,也对服务端渲染更友好。
# 自定义全局指令的生命周期
一个指令的定义对象可以提供几种生命周期/钩子函数 (都是可选的):
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
# 自定义指令声明
在create-vue (opens new window)脚手架环境下, 需要通过vue实例中的directive (opens new window) 注册全局指令的对象设置, 这里建议使用引入的方式进行设置
- 通过
import type
导入Vue实例提供的ts类型
// directives.ts
// 导入vue的类型
import type { App } from 'vue'
// 设置自定义指令 (全局)
export const water = (app: App) => {
app.directive('focus', { // focus为指令名称, 会被自动拼接为v-focus
mounted(el: HTMLElement) {
// 做一些事情...
},
})
}
然后在入口文件main.ts中导入自定义指令声明的方法
// main.ts
// 导入App.vue
import App from './App.vue'
import { createApp } from 'vue'
// 导入自定义指令
import { water } from './directives'
// 实例化Vue
const app = createApp(App)
// 调用导入的自定义指令
water(app)
在Vue中使用
<div v-focus />
查看目录图片
# 自定义指令简写
简化形式 (opens new window), 官方支持简写的方式, 自动会执行mounted
和 updated
两个生命周期, 但是如果想执行其他的就不行了
// 导入vue的类型
import type { App } from 'vue'
// 设置自定义指令简写方式 (局部)
export const waterSkip = (app: App) => {
app.directive('focuss', (el: any) => { // focuss为指令名称, 会被自动拼接为v-focus
// 这会在 `mounted` 和 `updated` 时都调用
// 做一些事情...
})
}
在Vue中使用
<div v-focus />
# 自定义指令拆分写法(常用)
以上都是作为一个导出方法来设置自定义指令的需要多次声明directive
, 可以只通过在入口文件main.js
声明directive
, 再用import
方式导入来使用
- 正常的自定义指令为对象
- 简写的自定义指定为方法
实现流程
我们通过export
导出自定义指令的对象(同理如果用简写, 那就是导出方法)
// directives.ts
export const waterClick = {
mounted(el: HTMLElement) {
// 做一个挂载完成后调用的事情...
}
}
再通过import
导入自定义指令
// main.ts
// 导入App.vue
import App from './App.vue'
import { createApp } from 'vue'
// 导入自定义指令
import { waterClick } from './directives'
// 实例化Vue
const app = createApp(App)
// 注册自定义指令
app.directive('focus', waterClick)
在Vue中使用
<div v-focus />
# 批量注册
如果有多个自定义指令的, 可以声明一个对象储存, 然后通过for...in
进行遍历, 取key值来进行注册
- 那么key的值就是自定义指令的名称
v-key
- 如果是ts, 需要用
interface
来规定key的类型为string
, 否则会被ts识别为隐式具有 “any“类型
// main.ts
// 导入App.vue
import App from './App.vue'
import { createApp } from 'vue'
// 导入自定义指令
import { waterClick } from './directives'
// 实例化Vue
const app = createApp(App)
// 给对象声明一个接口, 规定遍历的时候key为string类型, value为any类型
interface EachKey {
[key: string]: any
}
// 批量注册自定义指令, 绑定接口规定的类型
const directives: EachKey = {
waterClick
}
// 遍历对象, 注册自定义指令
for (const key in directives) {
// 这个时候自定义指令的名称为v-key的名称
app.directive(key, directives[key])
}
// 其他的实现方式
// Object.keys把对象的key转换为数组,然后进行数组遍历, 注册自定义指令
// Object.keys(directives).forEach((key) => {
// 这个时候自定义指令的名称为v-key的名称
// app.directive(key, directives[key]) // 注册自定义指令
// })
在Vue中使用
<div v-waterClick />