继《vue之全局请求loading》之后,总觉得全局loading有时候不太…友好,所以总想将loading加到被点击的元素上面,于是乎就想到了点击事件与请求方法相关联,本想重写组件的click方法,但是这样对组件的影响太大,所以放弃了,最终想到了Vue全局指令。
一、实现原理
通过全局指令代替组件事件,在全局指令中绑定click事件,当dom被点击时,给dom添加禁用状态,利用闭包等方式获取到绑定的方法,通过await等待方法执行完毕然后去掉禁用;
二、使用
@click
替换为v-con-click
<el-button type="primary" icon="el-icon-search" size="mini" v-con-click="handleQuery">搜索</el-button>
三、注意事项
1. 同步和异步回调只能选一种
同步通过await来等待去除禁用,而异步回调则是由自己判断去掉禁用。
同步(没有传参):
handleQuery(e) {
console.log(e); // 没有传参时,默认为点击事件
this.queryParams.pageNum = 1;
await this.getList();
}
异步回调(没有传参):
handleQuery() {
console.log(e); // 没有传参,默认为点击事件
return (removeDisabled) => {
this.queryParams.pageNum = 1;
this.getList().then(res => {
// 去掉组件禁用
removeDisabled();
})
}
}
2. 异步回调需闭包
因需要回传回调方法,所以需添加一层闭包;
3. 有传参的方法需闭包
因有参数的方法相当于绑定时会自动执行一遍,所以添加闭包,来获取执行代码。
handleQuery(d) {
return (removeDisabled) => {
this.queryParams.pageNum = 1;
this.getList().then(res => {
// 去掉组件禁用
removeDisabled();
})
}
}
所以对于有参数的方法异步回调就相当于两次闭包
handleQuery(d) {
return (e) => {
console.log(e); // 点击事件
return (removeDisabled) => {
// 去掉组件禁用
removeDisabled();
}
}
}
3. 其他问题
因想法特殊,可能会出现未知问题,目前暂未发现其他问题,如有问题,欢迎即使提出。
四、代码
const conClickDirective = {
bind(el, binding, vnode) {
el.addEventListener('click', async e => {
let hasCb = false;
el.classList.add('is-disabled');
el.disabled = true;
// 这里写点击事件的逻辑
if (binding.value instanceof Function) {
const res = await binding.value(e);
if (res instanceof Function) {
const d = res(() => {
el.disabled = false;
el.classList.remove('is-disabled');
})
if (!(d instanceof Promise)) {
hasCb = true;
} else {
await d;
}
}
}
if (!hasCb) {
el.disabled = false;
el.classList.remove('is-disabled');
}
});
}
};
// 全局注册自定义指令
Vue.directive('con-click', conClickDirective);