目的
防止重复提交表单,所以设置了一个loading值来控制。 代码修改如下。
function setLoading(loading) {
return function(target, name, descriptor) {
const old = descriptor.value;
descriptor.value = function() {
// 获取vue/react 组件的isLoading属性
const isLoading = this.data[loading];
if(isLoading) return;
this.data[loading] = true;
old.apply(this, arguments);
// 重置loading
this.data[loading] = false;
// 更新data到dom
this.$update();
};
return descriptor;
}
}
@setLoading('isLoading')
async getUserList() {
// 请求后台接口
const res = await backend.getUserList();
if(res) {
// 请求成功后的业务逻辑
}
}
有两个问题:
- 不知道我这个setLoading装饰器写的是不是对的。我看别人都是最后得return old.apply(this, arguments);但是我得将原来函数中间的逻辑在中间执行呃。。
- 由于getUserList是异步的函数,所以装饰器中的this.$update()这一步会提前backend.getUserList执行了。。最终导致数据请求回来了,但是dom无法更新了。
求大佬解答一下。。我在想可不可以在装饰器中加一个settimeout。。强行延迟this.$update(),不过感觉这样太low了
第三个问题: 我想基于上面的setLoading代码,实现如果请求时间大于300ms,才会显示loading的效果。
function setLoading(loading) {
let timer = null;
return function(target, name, descriptor) {
const old = descriptor.value;
descriptor.value = function() {
// 获取vue/react 组件的isLoading属性
const isLoading = this.data[loading];
if(isLoading) return;
// 这里设置一个300ms的定时器
timer = setTimeout(() => this.data[loading] = true, 300);
old.apply(this, arguments);
// 重置loading与定时器timer
clearTimeout(timer);
this.data[loading] = false;
// 更新data到dom
this.$update();
};
return descriptor;
}
}
然后我试了一下,手动把chrome网速调为slow 3g(网速很慢,请求时间 > 300ms). 效果符合预期,dom在loading结束后更新了data。但是正常网速下(请求时间<<<300ms),loading不会出现,数据也请求到了,但是dom的data并没有更新。
方法装饰器简单介绍
方法的装饰器是通过修改原有的描述对象,返回新的描述对象来替换原有的方法.
解答
old.apply(this, arguments),如果不需要有返回值,那就可以不用返回因为原来的方法是异步的,所以如果想要让
update在后面执行的话,第一种方法用async await关键词,让原来的方法变为同步的.第二种方法就是把update放在原先方法的then里面执行补充
不会更新 dom 的话,你可以试试看分别在
getUserList和update输出看看是谁先执行的.你评论里提到了
react,我猜测你是用react的,getUserList没有返回值,那可能是通过state来同步你的数据的,而setState是一个异步操作,如果你在getUserList里面用了setState的话,也需要加上await来等待setState执行完成.否则更新的速度比较快,state还没来得及更改,所以 dom 不更新.只是一般
react都是直接用setState来触发渲染的,而你还有个update所以不太确定.设置延时代码,你是要等请求之后等待 300ms 的,所以
setTimeout应该写在后面,另外要添加async和await关键词,setTimeout是执行一次就销毁的,一般也不用去清理一点小经验
当修改的方法为箭头函数的时候,描述符对象会不一样,需要做别的适配,因为太麻烦了,所以我一般都是直接用
function,但是又不想每次用方法的时候都用myclass.myFun()的方式去调用,而是想直接用myFun()去调用,那就需要在外层在调用一个装饰器用来绑定this了,这样就不会丢失this,core-decorators的autobind很好用.参考文章