活用 async-await,讓 Vue 變得更好用的裝飾器!
作者:OLong
https://segmentfault.com/a/1190000037604556
下文三個裝飾器,都是利用了 async/await 把異步變成同步的特性實現的。
要求被裝飾的方法必須寫成 async/await,用起來十分方便,實現徹底被隱藏在了裝飾器內部。
前兩個都是用在 ts 環境下 class 寫法的 vue 裏的。不過看清楚邏輯後,很容易修改成可以用在 js 環境中的 vue 組件上。
- 給 vue 添加一個指示初始化完成的變量。
指業務相關的初始化邏輯都完成了 比如搜索功能:搜索中顯示 loading,結果爲空時顯示暫無數據。但是第一次打開頁面時,搜索還沒完成,但顯示暫無數據又不合適 這個時候就需要一個這樣的變量處理邊界情況 用於 ts 環境下的 vue
通過裝飾器添加這個屬性,幷包裝 vue 的 created, mounted 和 beforeDestroy 方法。當 created 或者 mounted 裏發出的請求完成後,就把 pageIsReady 設爲 true。使用這個裝飾器時,在業務代碼中完全無感,沒有任何心智負擔。
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
}
- 給事件回調函數和按鈕 Dom 添加防抖與 loading 樣式
用於 ts 環境下的 vue
通過裝飾器包裝被裝飾的方法。要求被包裝的方式是 async/await 的。這樣裝飾器內只需要用一個 await 就可以得知被包裝的方法是否執行完成。同時,可以從事件對象中拿到被點擊的 dom 元素並修改它。
/*
* 請保證被包裝的方法的參數列表最後一個是點擊事件的參數
*/
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 屬性
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());
}
}
- mounted 之前顯示白屏
用於 js 的 vue 中包裝 vue 的對象
同上,通過 async/await 獲得 mounted 或者 created 是否執行完成 再通過指向 vue 實力的 this 拿到組件根節點,然後按需修改它 以下代碼只是將組件隱藏了,實際上可以寫更復雜的邏輯,在加載過程中顯示其他內容,畢竟拿到了 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;
}
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/c8apwepC0walXTrexBlfOg