一、什么是自定义指令
在vue官方文档中是这样描述的,自定义指令主要是为了重用涉及普通元素的底层DOM访问的逻辑。自定义指令主要分为全局自定义指令和局部自定义指令。
二、自定义指令相关参数
Vue 2.X钩子函数:
- bind:自定义指令绑定到DOM后调用。只调用一次,指令第一次绑定到元素的调用。在这里可以进行一次性的初始化设置。注意:只是加入进了DOM,但是渲染没有完成。
- inserted:自定义指令所在DOM,插入到父DOM后调用,渲染已经完成(父节点存在即可调用,不必存在于document中)。
- update:元素更新,但子元素尚未更新,将调用此钩子(自定义指令所在组件更新时执行,但是不保证更新完成),和自定义指令所在组件有关。
- componentUpdated:组件和子级更新后执行(自定义指令所在组件更新完成,且子组件也完成更新)。
- unbind:解绑(销毁)(自定义指令所在DOM销毁时执行),只调用一次。
Vue 3.X钩子函数:
- created:自定义指令所在组件,创建后调用。
- beforeMount:相当于Vue 2.X中的bind,当元素被插入到DOM前调用。
- mounted:相当于Vue 2.X中的inserted,当绑定元素的父组件被挂载后调用。
- beforeUpdate:绑定元素的父组件更新前调用。
- updated:相当于Vue 2.X中的componentUpdated,在绑定元素的父组件及他自己的所有子节点都更新后调用。
- beforeUnmount:绑定元素的父组件卸载前调用。
- unmounted:绑定元素的父组件卸载后调用。
指令的钩子会传递以下几种参数:
el
:指令绑定到的元素。这可以直接操作DOM。binding
:一个对象,包含以下属性。value
:传递给指令的值。例如在v-my-directive="1"
中,值是1。oldValue
:之前的值,仅在beforeUpdate
和updated
中可用。无论值是否更改,它都可用。arg
:传递给指令的参数(如果有的话)。例如在v-my-directive:add
中,参数是"add"。
modifiers
:一个包含修饰符的对象(如果有的话)。例如在v-my-directive:foo:bar
中,修饰符对象是{ foo: true, bar: true }。
instance
:使用该指令的组件实例。dir
:指令的定义对象。
vnode
:代表绑定元素的底层VNode。虚拟DOM节点,一个真实DOM元素的蓝图,对应el。prevNode
:上一个虚拟节点。之前的渲染中代表指令所绑定元素的VNode。仅在beforUpdate
和updated
钩子中使用。
三、注册自定义指令
自定义指令要分全局自定义指令和局部指令(以注册一个v-focus
指令为例子)
全局指令:通过应用实例注册一个全局自定义指令
Vue.directive(指令名, { 自定义指令生命周期 })
// Vue 2.X
import Vue from 'vue'
Vue.directive('focus', {
inserted: function(el) {
el.focus()
}
})
// Vue 3.X
const app = createApp({})
app.directive('focus', {
mounted(el) {
el.focus()
}
})
局部指令:可在组件中配置directives
选项来注册局部指令
directives(指令名, { 自定义指令生命周期 })
// Vue 2.X
directives: {
focus: {
inserted: function(el) {
el.focus()
}
}
}
// Vue 3.X
directives: {
focus: {
mounted(el) {
el.focus()
}
}
}
四、批量注册
1、创建专门放指令的文件夹directives,在文件夹中创建index.js文件。
2、在index.js文件中将所有指令引入后,写到一个对象中,并导出。
import debounce from './debounce'
import focus from './focus'
const directives = {
debounce,
focus
}
export default {
install(Vue) {
Object.keys(directives).forEach(key => {
Vue.directive(key, directives[key])
})
}
}
3、 在main.js文件中引入
// Vue 2.X
import Vue from 'vue'
import Directive from './directives'
Vue.use(Directive)
// Vue 3.X
import { createApp } from 'vue'
import App from './App.vue'
import Directive from './directives'
const app = createApp(App)
app.use(Directive)
app.mount('#app')
五、常用指令
1、防抖指令 v-debounce
定义:n 秒后再执行该事件,若在 n 秒内被重复触发,则重新计时。
场景:项目开发中,经常会遇到按钮多次点击后,重复请求接口,造成数据混乱,例如表单数据提交。
vue2写法:
const debounce = {
inserted: (el, binding) => {
// 没有绑定函数抛出错误
if (typeof binding.value !== 'function') {
throw 'debounce callback not a function'
}
let timer
el.addEventListener('click', () => {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
binding.value()
}, 1000)
})
}
}
export default debounce
vue3写法:
const debounce = {
mounted: (el, binding) => {
let timer
el.addEventListener('click', () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
binding.value()
}, 1000)
})
},
}
export default debounce
//使用场景
<template>
<button v-debounce="handleDebounce">防抖</button>
</template>
<script>
export default {
methods: {
handleDebounce() {
console.log('防抖,触发一次')
}
}
}
</script>
2、节流指令 v-throttle
定义: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效。
场景:多数在监听页面元素滚动事件的时候会用到。因为滚动事件,是一个高频触发的事件。
const throttle = {
bind: (el, binding) => {
// 没有绑定函数抛出错误
if (typeof binding.value !== 'function') {
throw 'throttle callback not a function'
}
// 开关
el._flag = true
el._timer = null
el._handler = () => {
if (!el._flag) return
// 函数执行后关闭开关
el._flag && binding.value()
el._flag = false
if (el._timer) clearTimeout(el._timer)
el._timer = setTimeout(_ => {
el._flag = true
}, 1000)
}
el.addEventListener('scroll', el._handler)
},
unbind: (el, binding) => {
el.removeEventListener('scroll', el._handler)
}
}
export default throttle
3、滚动到底部监听v-scroll-btm
Vue.directive('scroll-btm', {
inserted(el, binding) {
const { onScrollBtm, getContainer } = binding.value // onScrollBtm 触底回调,getContainer 获取滚动的容器,若无则自动选择挂载的dom
const target = getContainer ? getContainer() : el
if (!target.addEventListener) {
console.warn('请确认传递的容器为Dom!')
return
}
onScrollBtm && target.addEventListener('scroll', () => {
if (target.scrollHeight - target.scrollTop - target.clientHeight < 1) {
onScrollBtm() // 到底回调
}
})
}
})
<template>
<div id="tableScroll">
<a-table v-scroll-btm="{getContainer: getEl ,onScrollBtm: handleToBtm}" :pagination="false"></a-table>
</div>
</template>
getEl () {
return document.getElementById('tableScroll').getElementsByClassName('ant-table-body')[0]
},
//滑动至底部 触发事件
handleToBtm () {
if (this.pageOpt.total > this.dataSource.length) {
this.pageOpt.current++
this.getDeviceList()
}
},
4、按钮权限
Vue.directive('perm-btn', {
inserted(el, binding) {
if (!permissionJudge(binding.value) && el.parentNode) {
el.parentNode.removeChild(el);
}
function permissionJudge(value: string) {
// 此处store.getters.getMenuBtnList代表vuex中储存的按钮菜单数据
let list = store.getters.perm_btn
for (let item of list) {
if (item === value) {
return true
}
}
return false
}
}
})
<Button type="primary" v-perm-btn="50"></Button>
标签:el,Vue,自定义,vue,binding,focus,指令
From: https://blog.csdn.net/junsens/article/details/136938865