vue自定义指令

在Vue中除了核心功能默认的内置指令v-html、v-show外,也允许注册自定义指令。在某些场景下,仍然需要对普通DOM元素进行操作,需要在多个地方使用时,可以使用自定义指令简化代码,提高编程效率。Vue自定义指令有全局注册和局部注册两种方式。

自定义指令的声明方式

  • 全局自定义指令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 一般写在 main.js 中 /
    // directive.js
    import Vue from 'vue'
    Vue.directive('focus', { // 注册一个全局自定义指令 `v-focus`
    inserted: function (el) {
    el.focus()
    }
    })
    // main.js 引入
    import './directive/directive.js'
    // 模板中使用 <input type="text" v-focus>
  • 局部自定义指令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 在单个 .vue文件中 使用 directives 声明
    export default {
    directives: { // 注册局部自定义指令
    focus: {
    inserted: function (el) {
    el.focus()
    }
    }
    }
    }

自定义指令钩子函数及参数

v-自定义指令名称:参数.修饰符="表达式"

  • 钩子函数

    1
    2
    3
    4
    5
    // bind:只调用一次,指令第一次绑定到元素时调用。初始化
    // inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
    // update:所在组件的 VNode 更新时调用
    // componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用
    // unbind:只调用一次,指令与元素解绑时调用
  • 钩子函数的参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <!-- el: 指令所绑定的元素,可以用来直接操作 DOM -->
    <!-- binding: 一个对象 包含: name(指令名) value(指令的绑定值) arg(传给指令的参数) modifiers(指令修饰符) -->
    <div v-tips:text.stop="msg">自定义指令</div> <!-- msg: '参数' -->
    <script>
    export default {
    directives: {
    tips: {
    inserted: function (el, binding) {
    console.log(el); // <div>自定义指令</div>
    console.log(binding);
    /* {
    name: 'tips', // 指令名称
    arg: 'text', // 传给指令的参数
    value:'参数', // 指令的绑定值
    modifiers: { stop: true }, // 指令修饰符
    } */
    }
    }
    }
    }
    </script>
  • 动态指令参数 v-mydirective:[argument]="value"

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <p v-pin:[direction]="200">at 200px to the left.</p>
    <script>
    export default {
    data: function () {
    return { direction: 'left' }
    }
    }
    Vue.directive('pin', {
    bind: function (el, binding, vnode) {
    el.style.position = 'fixed'
    var s = (binding.arg == 'left' ? 'left' : 'top')
    el.style[s] = binding.value + 'px'
    }
    })
    </script>

Vue3中的自定义指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import directive from './directive.js'
const app = createApp(App)
directive(app)
app.mount('#app')
// 使用 <div v-tips:text.stop="msg">自定义指令</div>
// directive.js
export default function directive(app) {
app.directive('tips', {
// vue3自定义指令的钩子函数变更了 类似于vue3的comsitionApi的生命周期 钩子参数少了name
// 在绑定元素的 attribute 前 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {}, // bind
// 在绑定元素的父组件及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) { // inserted
console.log(binding)
/* {
arg: 'text', // 传给指令的参数
value:'参数', // 指令的绑定值
modifiers: { stop: true }, // 指令修饰符
oldValue: undefined // 上一次更新时的值
} */
},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {}, // componentUpdate
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {} // unbind
})
}

v-focus 自动获取焦点

1
2
3
4
5
6
7
8
<input type="text" v-focus />
<script>
Vue.directive('focus', {
inserted: function (el, binding) {
el.focus()
}
})
</script>

v-showTips 封装tips提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 简化页面多出用到的 tips 提示信息
Vue.directive('showTips', {
inserted(el, binding) {
let tips = {
'cardTips': '按照相关规则, 交易将按照同卡进出规则....',
'productTips': '按照相关规则, 交易将按照同卡进出规则....'
}
let img = document.createElment('img')
img.src = '../../static/common/tips.png'
img.style.width = '0.16rem'
img.onload = () => {
img.addEventListener('click', e => {
// 也可以使用 v-showTips.stop="" stop修饰符 阻止冒泡
e.stopPropagation() // 阻止事件冒泡
Vue.$alert({
'title': '温馨提示',
'msg': tips[binding.value],
'singleBtn': '知道了'
})
})
el.appendChild(img)
}
}
})
// 使用 <div v-showTips="'cardTips'">同卡进出</div> 参数为字符串时必须加''

v-copy 实现文本复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// directive.js
import Vue from 'vue'
Vue.directive('copy', {
bind(el, binding) {
// 定义 $value 是为了 能在初始化和更新完成 及时获取数据
el.$value = binding.value
el.$handle = () => {
let text = el.$value, textarea, copyText = ''
if (!text) {
Vue.$toast('复制的内容不能是空!')
return
}
textarea = document.createElement('textarea')
textarea.value = text
textarea.readOnly = 'readonly'
// textarea.style.display = 'none' // 这样会拿不到复制的信息
textarea.style.cssText = 'opacity:0;position:fixed;left:-9999px'
document.body.appendChild(textarea)
textarea.select()
copyText = document.execCommand('Copy')
if (copyText) { Vue.$toast('内容已复制到成功') }
document.body.removeChild(textarea)
}
el.addEventListener('click', el.$handle)
},
componentUpdated(el, binding) {
el.$value = binding.value // 数据更新同步 $value
},
unbind(el) {
el.removeEventListener('click', el.$handle)
}
})
// 使用 <h1 v-copy="text">点击复制</h1> text: "需要复制的内容"

v-limitInput 实现输入框限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 自定义事件
const trigger = (el, type) => {
let ev = document.createEvent('HTMLEvents')
ev.initEvent(type, true, true)
el.dispatchEvent(ev)
}
Vue.directive('limitInput', {
bind(el) {
el.$handle = () => {
let val = el.value, reg = /[^0-9a-zA-Z]/g
el.value = val.replace(reg, '')
trigger(el, 'input') // 同步到响应式数据中
}
el.addEventListener('keyup', el.$handle)
},
unbind(el) {
el.removeEventListener('keyup', el.$handle)
}
})
// 使用 <input type="text" v-limitInput />
-------------本文结束感谢您的阅读-------------
0%