活用 async/await ,讓 Vue 變得更好用的裝飾器!
下文三個裝飾器,都是利用了async/await把異步變成同步的特性實現(xiàn)的。
要求被裝飾的方法必須寫成async/await,用起來十分方便,實現(xiàn)徹底被隱藏在了裝飾器內(nèi)部。
前兩個都是用在ts環(huán)境下class寫法的vue里的。不過看清楚邏輯后,很容易修改成可以用在js環(huán)境中的vue組件上。
1. 給vue添加一個指示初始化完成的變量。
指業(yè)務(wù)相關(guān)的初始化邏輯都完成了 比如搜索功能:搜索中顯示loading,結(jié)果為空時顯示暫無數(shù)據(jù)。但是第一次打開頁面時,搜索還沒完成,但顯示暫無數(shù)據(jù)又不合適 這個時候就需要一個這樣的變量處理邊界情況 用于ts環(huán)境下的vue。
通過裝飾器添加這個屬性,并包裝vue的created, mounted和beforeDestroy方法。當created或者mounted里發(fā)出的請求完成后,就把pageIsReady設(shè)為true。使用這個裝飾器時,在業(yè)務(wù)代碼中完全無感,沒有任何心智負擔。
- import { Constructor } from"vue/types/options";
- export type WrapReadyProperty<T> = T & { pageIsReady?: boolean }
- /**
- * 在@compontent 之后使用這個裝飾器,
- * 組件就會被注入屬性 pageIsReady,
- * 當created和mounted都執(zhí)行完成時 pageIsReady 變成true,
- * 要求mounted或created是async/await。(取決于在哪個方法中發(fā)請求初始化組件)
- * 然后可以在template中直接使用。
- * 在script中使用調(diào)用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. 給事件回調(diào)函數(shù)和按鈕Dom添加防抖與loading樣式
用于ts環(huán)境下的vue。
通過裝飾器包裝被裝飾的方法。要求被包裝的方式是async/await的。這樣裝飾器內(nèi)只需要用一個await就可以得知被包裝的方法是否執(zhí)行完成。同時,可以從事件對象中拿到被點擊的dom元素并修改它。
- /*
- * 請保證被包裝的方法的參數(shù)列表最后一個是點擊事件的參數(shù)
- */
- 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;
- };
- }
用法: 在點擊事件函數(shù)上使用這個裝飾器。裝飾器會自動檢測該函數(shù)是否執(zhí)行完成,并在執(zhí)行過程中往按鈕的Dom節(jié)點上添加point:wait屬性。
- 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是否執(zhí)行完成 再通過指向vue實力的this拿到組件根節(jié)點,然后按需修改它 以下代碼只是將組件隱藏了,實際上可以寫更復雜的邏輯,在加載過程中顯示其他內(nèi)容,畢竟拿到了Dom,想干嘛就干嘛。
- 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;
- }