下文三个装饰器,都是利用了async/await把异步变成同步的特性实现的。
要求被装饰的方法必须写成async/await,用起来十分方便,实现彻底被隐藏在了装饰器内部。
前两个都是用在ts环境下class写法的vue里的。不过看清楚逻辑后,很容易修改成可以用在js环境中的vue组件上。
1、 给vue添加一个指示初始化完成的变量。
指业务相关的初始化逻辑都完成了 比如搜索功能:搜索中显示loading,结果为空时显示暂无数据。但是第一次打开页面时,搜索还没完成,但显示暂无数据又不合适 这个时候就需要一个这样的变量处理边界情况 用于ts环境下的vue
通过装饰器添加这个属性,并包装vue的created, mounted和beforeDestroy方法。当created或者mounted里发出的请求完成后,就把pageIsReady设为true。使用这个装饰器时,在业务代码中完全无感,没有任何心智负担。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
import { Constructor } from"vue/types/options"; export type WrapReadyProperty<T> = T & { pageIsReady?: boolean } /** * 在@compontent 之后使用这个装饰器, * 组件就会被注入属性 pageIsReady, * 当created和mounted都执行完成时 pageIsReady 变成true, * 要求mounted或created是async/await。(取决于在哪个方法中发请求初始化组件) * 然后可以在template中直接使用。 * 在script中使用调用isPageReady.call(this)方法; */ exportdefaultfunction PageReadyStatus() { let createdDone = false; let mountedDone = false; function isCreatedMountedAllDone() { return createdDone && mountedDone; } returnfunction pageReadyEnhancement<T extends Constructor>(target: T) { const oldMounted = target.prototype.mounted || function() { } const oldCreated = target.prototype.created || function() { } const oldBeforeDestroy = target.prototype.beforeDestroy || function() { } target.prototype.pageIsReady = false; target.prototype.created = asyncfunction(...params: any[]) { await oldCreated.apply(this, params); createdDone = true; this.pageIsReady = isCreatedMountedAllDone() } target.prototype.mounted = asyncfunction(...params: any[]) { await oldMounted.apply(this, params); mountedDone = true; this.pageIsReady = isCreatedMountedAllDone() } target.prototype.beforeDestroy = asyncfunction(...params: any[]) { await oldBeforeDestroy.apply(this, params); mountedDone = false; createdDone = false; this.pageIsReady = false; } return target }; } exportfunction isPageReady(this: WrapReadyProperty<Vue>) { returnthis.pageIsReady } |
2、给事件回调函数和按钮Dom添加防抖与loading样式
用于ts环境下的vue
通过装饰器包装被装饰的方法。要求被包装的方式是async/await的。这样装饰器内只需要用一个await就可以得知被包装的方法是否执行完成。同时,可以从事件对象中拿到被点击的dom元素并修改它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
/* * 请保证被包装的方法的参数列表最后一个是点击事件的参数 */ exportdefaultfunction buttonThrottle() { let pending = false; returnfunction(target: any, name: string): any { const btnClickFunc = target[name]; const newFunc = asyncfunction(this: Vue, ...params: any[]) { if (pending) { return; } const event:Event = params[params.length - 1]; let btn = event.target as HTMLElement pending = true; const recoverCursor = changeCursor(btn); try { await btnClickFunc.apply(this, params); } catch (error) { console.error(error); } recoverCursor(); pending = false; }; target[name] = newFunc; return target; }; } function changeCursor(btn?: HTMLElement) { if (btn == null) { return() => {}; } const oldCursor = btn.style.cursor; btn.style.cursor = "wait"; return() => { btn.style.cursor = oldCursor; }; } |
用法: 在点击事件函数上使用这个装饰器。装饰器会自动检测该函数是否执行完成,并在执行过程中往按钮的Dom节点上添加point:wait属性
1 2 3 4 5 6 7 8 9 10 |
import { Component, Vue } from"vue-property-decorator"; import buttonThrottle from"@/ui/view/utils/buttonThrottle"; type Member = { account_no: string; name: string; warn?: string }; @Component({ components: {} }) exportdefaultclass AddMemberInput extends Vue { @buttonThrottle() private async confirmAdd() { awaitthis.addMembers(this.getVaildMembers()); } } |
3、mounted之前显示白屏
用于js的vue中包装vue的对象
同上,通过async/await获得mounted或者created是否执行完成 再通过指向vue实力的this拿到组件根节点,然后按需修改它 以下代码只是将组件隐藏了,实际上可以写更复杂的逻辑,在加载过程中显示其他内容,毕竟拿到了Dom,想干嘛就干嘛。
1 2 3 4 5 6 7 8 9 |
function firstPaintControl(vueObj) { let oldMounted = vueObj.mounted || function() {}; vueObj.mounted = asyncfunction(...params) { this.$el.style.visibility = 'hidden'; await oldMounted.apply(this, params); this.$el.style.visibility = 'visible'; }; return vueObj; } |