继《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);
五、进阶版
进阶版:《Vue全局事件防止重复点击(等待请求)【进阶版】》