vue中有很多v- 前缀的特殊 attribute,这就是vue的指令。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。常见的vue指令有:v-bind、v-on、v-model、v-if、v-show等。但是有些时候vue内置的这些指令并不能满足我们的使用需求,因此vue也允许注册自定义指令,下面讲一下如何注册自定义指令。
一、定义:
// 一、在创建根实例之前 即全局自定义指令
// 一个指令定义对象有如下几个钩子函数:
// bind 只调用一次,指令第一次绑定到元素时调用。可以进行一次性的初始化设置。
// inserted: 被绑定的元素插入父节点的时候调用。
// update: 所在组件更新时调用。
// componentUpdate :所在组件更新后调用。
// unbind: 只调用一次,指令与元素解绑时调用。
// 上述钩子函数会被传入以下参数:
// el:指令所绑定的元素,可以用来直接操作 DOM。
// binding:一个对象,包含以下 property:
// -name:指令名,不包括 v- 前缀。
// -value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
// -expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
// -arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
// -modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
// vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
// 注意:除了 el 之外,其它参数都应该是只读的,切勿进行修改。
vue.directive('test', {
bind:(el,binding,vnode) => {
console.log('test, bind')
},
inserted:(el,binding,vnode) => {
console.log('test, inserted')
},
update:(el,binding,vnode) => {
console.log('test, update')
},
componentUpdated:(el,binding,vnode) => {
console.log('test, componentUpdated')
},
unbind:(el,binding,vnode) => {
console.log('test, unbind')
},
})
// 如果只想在bind和update时触发回调,那么可以这样写。
vue.directive('test', (el,binding,vnode) => {
console.log('test')
})
// 二、在一个组件的选项中定义自定义指令 即局部自定义指令
// 完整
directives: {
test: {
bind:(el,binding,vnode) => {
console.log('test, bind')
},
inserted:(el,binding,vnode) => {
console.log('test, inserted')
},
update:(el,binding,vnode) => {
console.log('test, update')
},
componentUpdated:(el,binding,vnode) => {
console.log('test, componentUpdated')
},
unbind:(el,binding,vnode) => {
console.log('test, unbind')
},
}
}
// 简写
directives: {
test: (el,binding,vnode) => {
console.log('test, bind')
}
}
使用:
// 在任何元素下都可以使用
<div v-test>test</div>
以下是一些常用的自定义指令,可放入工具函数模块中。
vue.directive('copy', { // 复制文字到剪切板
bind(el, binding) {
el.copyValue = binding.value
el.copyWords = () => {
if (!el.copyValue) {
console.log('无复制内容')
return
}
// 动态创建 textarea 标签
const textarea = document.createElement('textarea')
// 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
textarea.readOnly = 'readonly'
textarea.style.position = 'absolute'
textarea.style.left = '-9999px'
// 将要 copy 的值赋给 textarea 标签的 value 属性
textarea.value = el.copyValue
// 将 textarea 插入到 body 中
document.body.appendChild(textarea)
// 选中值并复制
textarea.select()
const result = document.execCommand('Copy')
if (result) {
console.log('复制成功')
}
document.body.removeChild(textarea)
}
// 绑定点击事件
el.addEventListener('click', el.copyWords)
},
// 当传进来的值更新的时候触发
componentUpdated(el, { value }) {
el.copyValue = value
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('click', el.copyWords)
},
})
vue.directive('long-press', { // 长按识别
bind (el, binding) {
// 回调函数
const longPress = (e) => {
binding.value(e)
}
// 计时器
let pressTimer = null
let start = (e) => {
console.log(e)
if (pressTimer === null) {
pressTimer = setTimeout(() => {
longPress()
}, 2000)
}
}
// 取消计时器
let cancel = (e) => {
console.log(e)
if (pressTimer !== null) {
clearTimeout(pressTimer)
pressTimer = null
}
}
// 添加事件监听器
el.addEventListener('mousedown', start)
el.addEventListener('touchstart', start)
// 取消计时器
el.addEventListener('click', cancel)
el.addEventListener('mouseout', cancel)
el.addEventListener('touchend', cancel)
el.addEventListener('touchcancel', cancel)
}
})
vue.directive('debounce', { // 防抖
inserted(el, binding) {
let timer = null
el.addEventListener('click', () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
binding.value()
}, 1000)
})
},
})