前言
在 vue 中,自定义指令是一种非常有用的功能,可以让我们轻松地扩展 vue 的行为。本文将介绍 vue 自定义指令的相关知识,帮助你更好地掌握 vue 的技能,让你的 vue 应用更加强大和灵活。
其实说到 vue
中的自定义指令,相信大家都不陌生。在官网中是这么说的,除了核心功能默认内置的指令 (v-model
和 v-show
),vue
也允许注册自定义指令。那什么时候会用到自定义指令呢?代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 dom
元素进行底层操作,这时候就会用到自定义指令。
自定义指令
自定义指令分为:全局自定义指令,局部自定义指令。它有两个参数:参数1:指令的名称;参数2:是一个对象,这个对象身上,有钩子函数。
全局自定义指令
通过
Vue.directive()
函数注册一个全局的指令
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
在
main.js
中引入
import focus from '@/utils/focus'
局部自定义指令
通过组件的
directives
属性,对该组件添加一个局部的指令
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
然后你可以在组件中任何元素上使用
v-focus
自定义的指令,如下:
<input v-focus/>
批量注册指令
在 src
目录下新建文件夹(directives/index.js
)管理各个子文件(自定义指令文件),然后在根文件(index.js
)中对外暴露 api
,最后在 main.js
中引入并注册,这样做的好处是统一管理后,后期比较好维护,而且会比较清晰。
文件目录:
src/directives/index.js
import Vue from 'vue';
import copy from './modules/copy';//引入自定义指令
const directives = {
// 自定义指令
copy,
};
export default {
install(Vue) {
Object.keys(directives).forEach(key => Vue.directive(key, directives[key]))
}
}
在
main.js
中引入并注册
import Directives from '@/directives';
Vue.use(Directives);
钩子函数
bind
只调用一次,指令第一次绑定到元素时调用。
inserted
被绑定元素插入父节点时调用。
update
所在组件的虚拟节点更新时调用。
componentUpdated
所在组件的虚拟节点及其子虚拟节点全部更新时调用。
unbind
只调用一次,指令与元素解绑时调用。
实例
看到这里,你可能对自定义指令已经有了大概的了解和认识,那可能有的同学要问了,在实际的开发中,自定义指令有什么使用场景吗?下面分享在开发中常用的使用场景。
1. 复制粘贴指令
文件目录:
src/directives/modules/copy.js
export default {
bind(el, {
value
}) {
el.$value = value;
el.handler = () => {
if (!el.$value) {
// 值为空的时候,给出提示
console.log('无复制内容');
return;
}
const textarea = document.createElement('textarea');
// 将该textarea设为readonly,防止iOS下自动唤起软键盘
textarea.readOnly = 'readonly';
textarea.style.position = 'absolute';
textarea.style.left = '-9999px';
// 将要copy的值赋给textarea标签的value属性
textarea.value = el.$value;
// 将textarea插入到body中
document.body.appendChild(textarea);
// 选中值并复制
textarea.select();
const result = document.execCommand('Copy');
if (result) {
console.log('复制成功');
}
// 赋值成功后,将textarea移除掉
document.body.removeChild(textarea);
};
// 绑定点击事件
el.addEventListener('click', el.handler);
},
componentUpdated(el, {
value
}) {
el.$value = value;
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('click', el.handler);
}
}
组件中使用
<template>
<div>
<button v-copy="copyText">复制</button>
</div>
</template>
<script>
export default {
data() {
return {
copyText: "等待被复制的内容",
};
},
};
</script>
实现效果
2. 长按指令
文件目录:
src/directives/modules/longpress.js
export default {
bind(el, {
value
}) {
if ('function' !== typeof value) {
throw 'callback must be a function';
}
let pressTimer = null;
let start = e => {
if ('click' === e.type && 0 !== e.button) {
return;
}
if (null === pressTimer) {
pressTimer = setTimeout(() => {
// 执行函数
value(e);
}, 1000);
}
};
let cancel = e => {
if (null !== pressTimer) {
clearTimeout(pressTimer);
pressTimer = null;
}
};
// 添加事件监听器
el.addEventListener('mousedown', start);
el.addEventListener('mouseout', cancel);
el.addEventListener('click', cancel);
el.addEventListener('touchstart', start);
el.addEventListener('touchend', cancel);
el.addEventListener('touchcancel', cancel);
}
}
组件中使用
<template>
<div>
<button v-longpress="longpress">长按我</button>
</div>
</template>
<script>
export default {
methods: {
longpress() {
alert("触发了长按");
},
},
};
</script>
实现效果
3. 输入框防抖指令
文件目录:
src/directives/modules/debounce.js
export default {
inserted(el, {
value
}) {
if ('function' !== typeof value) {
throw 'directive value must be function';
}
let timer;
el.addEventListener('keyup', () => {
timer && clearTimeout(timer);
timer = setTimeout(() => {
value && value();
}, 1000);
});
}
}
组件中使用
<template>
<div>
<button v-longpress="longpress">长按我</button>
</div>
</template>
<script>
export default {
methods: {
longpress() {
alert("触发了长按");
},
},
};
</script>
实现效果
4. 输入框自动聚焦指令
文件目录:
src/directives/modules/focus.js
export default {
inserted(el, {
value
}) {
// 聚焦元素
el.focus()
}
}
组件中使用
<template>
<div>
<input v-focus type="text" />
</div>
</template>
实现效果
5. 全屏指令
文件目录:
src/directives/modules/fullScreen.js
export default {
bind(el, binding) {
if (binding.modifiers.icon) {
if (el.hasIcon) return
// 创建全屏图标
const iconElement = document.createElement('i')
iconElement.setAttribute('class', 'el-icon-full-screen')
iconElement.setAttribute('style', 'margin-left:5px')
el.appendChild(iconElement)
el.hasIcon = true
}
el.style.cursor = el.style.cursor || 'pointer'
// 监听点击全屏事件
el.addEventListener('click', () => handleClick())
}
}
function handleClick() {
let FullScreen = false
let element = document.documentElement;
if (FullScreen) {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
} else {
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.webkitRequestFullScreen) {
element.webkitRequestFullScreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
}
}
FullScreen = !FullScreen;
}
组件中使用
<template>
<div>
<span v-fullScreen.icon>全屏</span>
</div>
</template>
实现效果
6. 文字超出展示省略号指令
文件目录:
src/directives/modules/ellipsis.js
export default {
bind(el, binding) {
console.log(el,binding);
el.style.width = binding.arg || 100 + 'vw'
el.style.whiteSpace = 'nowrap'
el.style.overflow = 'hidden';
el.style.textOverflow = 'ellipsis';
}
}
组件中使用
<template>
<div>
<div v-ellipsis>给你一首诗的时间,坐在炉火边,捧着诗卷阅读,听不见大摆钟的摇摆,远离岁月的尘埃</div>
</div>
</template>
实现效果
7. 拖拽指令
文件目录:
src/directives/modules/drag.js
export default {
bind(el, binding) {
document.onselectstart = function () {
return false //禁止选择网页上的文字
}
el.style.cursor = 'move' // 图标
el.style.position = 'absolute'
el.onmousedown = function (ev) {
// 用元素距离视窗的X、Y坐标,减去el元素最近的相对定位父元素的left、top值
var sX = ev.clientX - el.offsetLeft
var sY = ev.clientY - el.offsetTop
document.onmousemove = function (ev) {
var eX = ev.clientX - sX
var eY = ev.clientY - sY
// 不断地更新元素的left、top值
el.style.left = eX + 'px'
el.style.top = eY + 'px'
}
document.onmouseup = function () {
// 清除mousemove事件
document.onmousemove = null
}
}
}
}
组件中使用
<template>
<div>
<div class="box" v-drag>拖拽</div>
</div>
</template>
<script>
export default {
data() {
return {};
},
};
</script>
<style scoped>
.box {
width: 100px;
height: 100px;
background: cadetblue;
text-align: center;
line-height: 100px;
color: #fff;
}
</style>
实现效果
8. 字符串整形指令
文件目录:
src/directives/modules/format.js
export default {
bind(el, binding, vnode) {
const {
value,
modifiers
} = binding
if (!value) return
let formatValue = value
if (modifiers.toFixed) {
formatValue = value.toFixed(2)
}
console.log(formatValue)
if (modifiers.price) {
formatValue = formatNumber(formatValue)
}
el.innerText = formatValue
},
}
function formatNumber(num) {
num += '';
let strs = num.split('.');
let x1 = strs[0];
let x2 = strs.length > 1 ? '.' + strs[1] : '';
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
return x1 + x2
}
组件中使用
<template>
<div>
<div v-format.toFixed.price="123456789"></div>
<div v-format.toFixed="123.4567"></div>
</div>
</template>
实现效果
持续更新中...