在实际项目开发中,自定义指令用得还是比较多的,比如:复制粘贴、输入框防抖、输入框禁止特殊字符、权限校验、背景水印、拖拽等等…
指令确实是个优雅的存在。
Vue3中定义一个普通的自定义指令的详细说明参见官网:https://cn.vuejs.org/guide/reusability/custom-directives.html
一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会接收到指令所绑定元素作为其参数。
自定义指令可选钩子
const myDirective = {
// 在绑定元素的 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) {}
}
钩子参数
指令的钩子会传递以下几种参数:
-
el
:指令绑定到的元素。这可以用于直接操作 DOM。 -
binding
:一个对象,包含以下属性。value
:传递给指令的值。例如在v-my-directive="1 + 1"
中,值是2
。oldValue
:之前的值,仅在beforeUpdate
和updated
中可用。无论值是否更改,它都可用。arg
:传递给指令的参数 (如果有的话)。例如在v-my-directive:foo
中,参数是"foo"
。modifiers
:一个包含修饰符的对象 (如果有的话)。例如在v-my-directive.foo.bar
中,修饰符对象是{ foo: true, bar: true }
。instance
:使用该指令的组件实例。dir
:指令的定义对象。
-
vnode
:代表绑定元素的底层 VNode。 -
prevNode
:之前的渲染中代表指令所绑定元素的 VNode。仅在beforeUpdate
和updated
钩子中可用。
下面是一个自定义指令的例子,给需要的元素加上我们自定义的水印,同时说明如何把这个水印指令注册到全局。
自定义水印指令
昨天刚学习了canvas,刚好拿水印指令练个手,主要思路:
- 生成一个canvas画布
- 在canvas上绘制出水印,
- 给需要增加水印的元素,增加background-image属性,属性值是通过canvas生成base64
准备工作
首先在src中新建directive文件夹
watermark.ts是水印指令代码
index.ts是指令统一出口
watermark.ts
import { DirectiveBinding } from 'vue'
// 水印
export default {
mounted(el: HTMLElement, binding: DirectiveBinding) {
let txt = 'hello world'
let style = {}
if (binding && binding.value) {
txt = binding.value.txt
style = binding.value.style
}
genWatermark(el, txt, style)
}
}
interface CanvasTextStyle {
font?: string
color?: string
}
function genWatermark(el: HTMLElement, txt: string, style?: CanvasTextStyle) {
const defaultStyle = {
font: '14px arial',
color: 'rgba(0,0,0,0.2)'
}
let canvas = <HTMLCanvasElement>document.createElement('canvas')
let ctx = <CanvasRenderingContext2D>canvas.getContext('2d')
ctx.translate(150, 75)
ctx.rotate((Math.PI / 180) * 25)
ctx.translate(-150, -75)
ctx.font = style?.font || defaultStyle.font
ctx.fillStyle = style?.color || defaultStyle.color
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText(txt, canvas.width / 2, canvas.height / 2)
el.style.backgroundImage = `url(${canvas.toDataURL('image/png')})`
}
全局注册指令
为了使用方便,我们选择把水印指令注册到全局。
首先通过index.ts统一导出
directive/index.ts
import { App } from 'vue'
import color from './color'
import watermark from './watermark'
export default (app: App) => {
app.directive('color', color)
app.directive('watermark', watermark)
}
在main.ts中导入
import App from './App.vue'
const app = createApp(App)
...
// 注册指令
import directive from './directive'
directive(app)
app.mount('#app')
最后,在组件中使用
<div v-watermark></div>
<div
v-watermark="{
txt: 'mocha vue3 system',
style: {
font: '10px gothic',
color: 'rgba(255,80,0,0.5)'
}
}"
></div>
最后的成品,和想要达到的效果还是有一点差距,无奈canvas学艺不精,只能让它们这样整整齐齐的排列了。
一个指令当然是不能满足需要的,接下来本项目还要加入更多常用指令,你觉得还有哪些很有用的指令呢?
更多Vue3自定义指令
# Element Plus 的表格el-table高度自适应