前言
前面有专门的文字,讲过Vue指令,以及如何使用指令,今天就来讲讲如何添加全局指令,并且附上2个非常适用的例子。
如何添加全局指令?
在上面文章中,提到过一种方法,在main.js(入口JS文件)中引入你已经写好的指令文件,可以省略文件后缀:
// main.js
import focus from 'xxx/directive'
如果你有多个指令文件了?怎么引入?
Vue.use((Vue) => {
((requireContext) => {
const arr = requireContext.keys().map(requireContext);
(arr || []).forEach((directive) => {
directive = directive.__esModule && directive.default ? directive.default : directive;
Object.keys(directive).forEach((key) => {
Vue.directive(key, directive[key]);
});
});
})(require.context('../directives', false, /^\.\/.*\.js$/));
});
这里用到了require.context函数,require.context是webpack中,用来创建自己的(模块)上下文。
require.context函数接收三个参数:
1、要搜索的文件夹目录
2、是否还应该搜索它的子目录
3、以及一个匹配文件的正则表达式
我们搜索directives目录下的所有js文件,遍历装载指令。
下面我们来看看2个实用的自定义指令。
vue非本元素点击事件指令
这个指令的的作用是什么?
比如:一个按钮点击后弹出一个浮层,然后点击按钮外的所有事件,都关闭浮层。
export default {
clickOut: {
// 初始化指令
bind(el, binding, vnode) {
function clickHandler(e) {
// 这里判断点击的元素是否是本身,是本身,则返回
if (el.contains(e.target)) {
return false;
}
// 判断指令中是否绑定了函数
if (binding.expression) {
// 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
binding.value(e);
}
}
// 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
el.__vueClickOutside__ = clickHandler;
document.addEventListener('click', clickHandler);
},
update() {},
unbind(el, binding) {
// 解除事件监听
document.removeEventListener('click', el.__vueClickOutside__);
delete el.__vueClickOutside__;
}
}
}
然后,在main.js中这个指令,就可以使用了。
<span @click="showDialog" v-click-out="hideDialog">点击我打开否则关闭</span>
vue倒计时指令
var toZeroStr = (val, num = 2) => {
num = num || 2;
return (new Array(num)
.join('0') + val)
.slice(-num);
}
var milliseconds2HMS = (diff) => {
const millisecond = diff % 1000;
diff = diff - millisecond;
return seconds2HMS(diff / 1000);
}
var seconds2HMS = (diff) => {
const seconds = diff % 60;
const minutes = (diff - seconds) % 3600;
// const hours = (diff - minutes - seconds) % 86400;
const hours = diff - minutes - seconds;
return [hours / 3600, minutes / 60, seconds];
}
export default {
// 倒计时
countdown: {
bind(el, binding, vnode) {
const { componentOptions, data } = vnode;
const listeners = componentOptions ? componentOptions.listeners : null;
const on = data ? data.on : null;
const events = listeners ? listeners : on ? on : null;
if (events && typeof events === 'object' && Object.keys(events).length) {
binding.customListeners = events;
}
},
inserted(el, binding, vnode) {
let val = +binding.value;
if (!val) {
return;
}
const formatter = vnode.data.attrs.formatter;
let timer = null;
window.clearInterval(el.timer);
const tFunction = () => {
val -= 1000;
let instance = milliseconds2HMS(val);
if (val <= 0) {
if (timer) {
window.clearInterval(timer);
timer = null;
if (binding.customListeners) {
binding.customListeners.complete && binding.customListeners.complete();
}
}
el.innerHTML = '0';
return;
}
el.innerHTML = formatter.replace(/(HH.+)(mm.+)(ss.+)/g, (str, $1, $2, $3) => {
return str.replace(new RegExp($1, 'g'), !instance[0] ? '' : $1.replace(/HH/g, toZeroStr(instance[0])))
.replace(new RegExp($2, 'g'), !instance[0] && !instance[1] ? '' : $2.replace(/mm/g, toZeroStr(instance[1])))
.replace(new RegExp($3, 'g'), !instance[1] && !instance[2] ? '' : $3.replace(/ss/g, toZeroStr(instance[2])));
});
};
tFunction();
timer = window.setInterval(tFunction, 1000);
el.timer = timer;
},
update(el, binding, vnode) {
if (binding.oldValue !== binding.value) {
window.clearInterval(el.timer);
binding.def.inserted(el, binding, vnode);
}
},
unbind(el, binding, vnode) {
window.clearInterval(el.timer);
el.timer = null;
delete el.timer;
const customListeners = binding.customListeners;
if (customListeners) {
delete binding.customListeners;
}
}
}
}
使用方法
<span v-countdown="10000" formatter='HH小时mm分ss秒'></span>
这里的v-countdown参数就是剩余秒数,如果你只有2个时间(起始结束时间),需要先行计算;formatter参数是时间格式。