来源 | github.com/Michael-lzg… 前天看到github上有人分享Vue自定义指令结合实际业务需求调整做出了优化版本,不喜勿喷。
在 Vue,除了核心功能默认内置的指令 ( v-model 和 v-show ),Vue 也允许注册自定义指令。它的作用价值在于当开发人员在某些场景下需要对普通 DOM 元素进行操作。
Vue 自定义指令有全局注册和局部注册两种方式。先来看看注册全局指令的方式,通过 Vue.directive( id, [definition] ) 方式注册全局指令。然后在入口文件中进行 Vue.use() 调用。
在 main.js 引入并调用
import Directives from './directives'
Vue.use(Directives)
批量注册指令,新建 directives/index.js 文件
import copy from './copy'
import longpress from './longpress'
import debounce from './debounce'
import spare from './spare'
// 自定义指令
const directives = {
copy, // 复制粘贴指令
longpress, // 长按指令
debounce, // 输入框防抖指令
spare // 图片加载失败显示备用图指令
}
export default {
install(Vue) {
Object.keys(directives).forEach(key => {
Vue.directive(key, directives[key])
})
}
}
指令定义函数提供了几个钩子函数(可选):
- bind: 只调用一次,指令第一次绑定到元素时调用,可以定义一个在绑定时执行一次的初始化动作。
- inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
- update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值。
- componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
- unbind: 只调用一次, 指令与元素解绑时调用。
下面分享几个实用的 Vue 自定义指令
- 复制粘贴指令 v-copy
- 长按指令 v-longpress
- 输入框防抖指令 v-debounce
- 图片加载失败显示备用图指令 v-spare
1、v-copy
需求:
实现一键复制文本内容,用于鼠标右键粘贴,部分场景可能需要有回调。
思路:
- 动态创建 textarea 标签,并设置 readOnly 属性及移出可视区域
- 将要复制的值赋给 textarea 标签的 value 属性,并插入到 body
- 选中值 textarea 并复制
- 将 body 中插入的 textarea 移除
- 在第一次调用时绑定事件,在解绑时移除事件
const copy = {
bind(el, binding) {
el.$value = binding.value
el.handler = () => {
// 判断值为空的时候,给出提示
if (typeof el.$value !== 'object') {
if (!el.$value) {
// 可根据项目UI设计默认提示
console.log('无复制内容')
return
}
} else {
if (!el.$value.text) {
// 有回调则执行回调函数
el.$value.callback(false)
return
}
}
// 动态创建 textarea 标签
const textarea = document.createElement('textarea')
// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
textarea.readOnly = 'readonly'
textarea.style.position = 'absolute'
textarea.style.left = '-9999px'
// 将要 copy 的值赋给 textarea 标签的 value 属性
if (typeof el.$value !== 'object') {
textarea.value = el.$value
} else {
textarea.value = el.$value.text
}
// 将 textarea 插入到 body 中
document.body.appendChild(textarea)
// 选中值并复制
textarea.select()
const result = document.execCommand('Copy')
if (typeof el.$value !== 'object') {
if (result) {
// 复制成功可根据项目UI设计默认提示
console.log('复制成功')
} else {
// 复制失败可根据项目UI设计默认提示
console.log('复制失败')
}
} else {
if (result) {
// 复制成功有回调则执行回调函数
el.$value.callback(true)
} else {
// 复制失败有回调则执行回调函数
el.$value.callback(false)
}
}
document.body.removeChild(textarea)
}
// 添加事件监听器
el.addEventListener('click', el.handler)
},
// 当传进来的值更新的时候触发
componentUpdated(el, binding) {
el.$value = binding.value
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('click', el.handler)
}
}
export default copy
// 传入String 无回调函数
// <div v-copy="'复制内容'">复制按钮</div>
// 传入Object 回调callback回调参数Boolean true成功 false 失败
// <div v-copy="{ text: '复制内容', callback: copyCallback }">复制按钮</div>
使用:
给 Dom 加上 v-copy 及复制的文本即可
<template>
<div>
<div v-copy="copyText">复制</div>
<div v-copy="{ text: copyText, callback: copyCallback }">复制</div>
</div>
</template>
<script>
export default {
data() {
return {
copyText: '复制内容'
}
},
methods: {
copyCallback(CallbackVal) {
console.log(CallbackVal)
}
}
}
</script>
2、v-longpress
需求:
实现长按,用户需要按下并按住按钮几秒钟,触发相应的事件
思路:
- 创建一个计时器, 2 秒后执行函数
- 当用户按下按钮时触发 mousedown 事件,启动计时器;用户松开按钮时调用 mouseout 事件。
- 如果 mouseup 事件 2 秒内被触发,就清除计时器,当作一个普通的点击事件
- 如果计时器没有在 2 秒内清除,则判定为一次长按,可以执行关联的函数。
- 在移动端要考虑 touchstart,touchend 事件
const longpress = {
bind: function(el, binding) {
el.$value = binding.value
// 定义计时器容器
let timer = null
// 创建计时器 (2秒后执行函数)
el.start = e => {
if (typeof el.$value !== 'function') {
throw 'callback must be a function'
}
if (e.type === 'click' && e.button !== 0) {
return
}
if (timer === null) {
timer = setTimeout(() => {
// 运行函数
el.$value()
}, 2000)
}
}
// 取消计时器
el.cancel = () => {
if (timer !== null) {
clearTimeout(timer)
timer = null
}
}
// 添加事件监听器
el.addEventListener('mousedown', el.start)
el.addEventListener('touchstart', el.start)
// 取消计时器
el.addEventListener('click', el.cancel)
el.addEventListener('mouseout', el.cancel)
el.addEventListener('touchend', el.cancel)
el.addEventListener('touchcancel', el.cancel)
},
// 当传进来的值更新的时候触发
componentUpdated(el, binding) {
el.$value = binding.value
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('mousedown', el.start)
el.removeEventListener('touchstart', el.start)
el.removeEventListener('click', el.cancel)
el.removeEventListener('mouseout', el.cancel)
el.removeEventListener('touchend', el.cancel)
el.removeEventListener('touchcancel', el.cancel)
}
}
export default longpress
// <div v-longpress="longpress">长按</div>
使用:
给 Dom 加上 v-longpress 及回调函数即可
<template>
<button v-longpress="longpress">长按</button>
</template>
<script>
export default {
methods: {
longpress() {
console.log('长按指令生效')
}
}
}
</script>
3、v-debounce
背景:
在开发中,有些提交保存按钮有时候会在短时间内被点击多次,这样就会多次重复请求后端接口,造成数据的混乱,比如新增表单的提交按钮,多次点击就会新增多条重复的数据。
需求:
防止按钮在短时间内被多次点击,使用防抖函数限制规定时间内只能点击一次。
思路:
- 定义一个延迟执行的方法,如果在延迟时间内再调用该方法,则重新计算执行时间。
- 将时间绑定在 click 方法上。
const debounce = {
bind: function(el, binding) {
el.$value = binding.value
// 定义计时器容器
let timer = null
el.handler = () => {
if (typeof el.$value !== 'function') {
throw 'callback must be a function'
}
// 取消计时器
if (timer) {
clearTimeout(timer)
timer = null
}
// 创建计时器 (1秒后执行函数)
if (timer === null) {
timer = setTimeout(() => {
// 运行函数
el.$value()
}, 1000)
}
}
// 添加事件监听器
el.addEventListener('keyup', el.handler)
},
// 当传进来的值更新的时候触发
componentUpdated: function(el, binding) {
el.$value = binding.value
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('keyup', el.handler)
}
}
export default debounce
// <input type="text" v-debounce="debounce">
使用:
给 Dom 加上 v-debounce 及回调函数即可
<template>
<button v-debounce="debounceClick">防抖</button>
</template>
<script>
export default {
methods: {
debounceClick() {
console.log('只触发一次')
}
}
}
</script>
4、v-spare
需求:
img请求加载不到网络图片后,新给img备用图片地址
思路:
- 监听img的error事件
- img加载错误后触发error事件,检验备用地址是否有效
- 如果备用有效,将备用地址赋值给img的src
const spare = {
bind(el, binding) {
el.$value = binding.value
el.handler = () => {
// 动态创建 img 标签
const img = document.createElement('img')
// 检验备用地址是否有效
img.onload = () => {
// 赋值新的图片地址
el.src = el.$value
}
img.onerror = () => {
console.log('备用图片无法加载')
}
img.src = el.$value
}
// 绑定事件
el.addEventListener('error', el.handler)
},
// 当传进来的值更新的时候触发
componentUpdated(el, binding) {
el.$value = binding.value
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('error', el.handler)
}
}
export default spare
// <img :src="imgScr" v-spare="spareImg" />
使用:
给 Dom 加上 v-spare 及备用的图片地址即可
<template>
<div>
<img src="http://www.xxx.com/" v-spare="spareImg" />
</div>
</template>
<script>
export default {
props: {},
data() {
return { spareImg: require('@/assets/logo.png') }
}
}
</script>
copy、longpress、debounce均为参考别人代码后补充完善
附源码地址:https://github.com/Michael-lzg/v-directives
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!