vue-async-click

解决的问题

我们经常遇到 点击 按钮来触发某个异步请求的情况, 比如 提交/确定 等按钮.

但这种情况我们又需要处理一些并发的问题, 防止用户连续频繁的触发异步

其他方案 (只列举几个)

  • 自己设置一个标志位, 来标识是否在请求中, 每次触发后对标志位判断

    缺点是需要额外设置标志位字段,重复性代码.降低代码可读性

  • 对点击触发的异步 进行 throttle 处理

    throttle设置的时间间隔是固定的, 没法估计请求实际使用的时间, 太短还是会连续触发. 太长体验很差

我的方案 async-click 指令

  • vue2:
Vue.directive("async-click", {
// 当被绑定的元素插入到 DOM 中时……
bind: function (el, binding, vnode) {
// 获取 bind 的异步方案名称
const fnName = binding.expression;
// 获取具体方法的引用
const fn = vnode.context && vnode.context[fnName];
if (!fn) {
console.error("async-click 指令需要 binding 一个方法");
}
// 设置内部的标志位
let start = false;
el.addEventListener(
"click",
e => {
// 对其他 click 时间阻止
e && e.stopImmediatePropagation();
if (!start) {
// 第一次点
console.debug("click flag ->", start);
start = true;
// 执行,并获得返回值
const rt = fn();
if (!(rt instanceof Promise)) {
console.error("async-click 指令绑定值错误:返回类型必须为 Promise");
}

console.debug(rt);
//! 此处需要特别注意, 必须是 Promise 完成, 不能再某种情况下永远处于 pending 状态
rt.finally(() => {
// promise 结束后重置标志位
console.debug("finally");
start = false;
});
}
},
true
);
}
});
  • vue3
    app.directive("async-click", {
    // 当被绑定的元素插入到 DOM 中时……
    created(el, binding) {
    // 获取 bind 的异步方案名称
    console.debug(binding);
    // const fnName = binding;
    // 获取具体方法的引用
    const fn = binding.value
    if (!fn) {
    console.error("async-click 指令需要 binding 一个方法");
    }
    // 设置内部的标志位
    let start = false;
    el.addEventListener(
    "click",
    (e: MouseEvent) => {
    // 对其他 click 时间阻止
    e && e.stopImmediatePropagation();
    if (!start) {
    // 第一次点
    console.debug("click flag ->", start);
    start = true;
    // 执行,并获得返回值
    const rt: Promise<unknown> = fn();
    if (!(rt instanceof Promise)) {
    console.error("async-click 指令绑定值错误:返回类型必须为 Promise");
    }

    console.debug(rt);
    //! 此处需要特别注意, 必须是 Promise 完成, 不能再某种情况下永远处于 pending 状态
    rt.finally(() => {
    // promise 结束后重置标志位
    console.debug("finally");
    start = false;
    });
    }
    },
    true
    );
    }
    });

使用侧例子:

`将原来 @click=xxx 的部分替换为 v-async-click=xxx


<van-button
v-async-click="demo"
round
class="pc-submit"
type="primary"
size="normal"
>提交</van-button>


methods:{
demo() {
console.info("demo start");
return new Promise(resolve => {
setTimeout(() => {
console.info("demo end");
resolve();
}, 3000);
});
}
}

未来可扩展部分:

  • 针对不同的事件
  • 可以带入参数 fn(arg)