活用async/await,讓Vue變得更好用的裝飾器!
作者:OLong
原文:https://segmentfault.com/a/1190000037604556
下文三個(gè)裝飾器,都是利用了async/await把異步變成同步的特性實(shí)現(xiàn)的。
要求被裝飾的方法必須寫成async/await,用起來(lái)十分方便,實(shí)現(xiàn)徹底被隱藏在了裝飾器內(nèi)部。
前兩個(gè)都是用在ts環(huán)境下class寫法的vue里的。不過(guò)看清楚邏輯后,很容易修改成可以用在js環(huán)境中的vue組件上。
1. 給vue添加一個(gè)指示初始化完成的變量。
指業(yè)務(wù)相關(guān)的初始化邏輯都完成了 比如搜索功能:搜索中顯示loading,結(jié)果為空時(shí)顯示暫無(wú)數(shù)據(jù)。但是第一次打開(kāi)頁(yè)面時(shí),搜索還沒(méi)完成,但顯示暫無(wú)數(shù)據(jù)又不合適 這個(gè)時(shí)候就需要一個(gè)這樣的變量處理邊界情況 用于ts環(huán)境下的vue
通過(guò)裝飾器添加這個(gè)屬性,并包裝vue的created, mounted和beforeDestroy方法。當(dāng)created或者mounted里發(fā)出的請(qǐng)求完成后,就把pageIsReady設(shè)為true。使用這個(gè)裝飾器時(shí),在業(yè)務(wù)代碼中完全無(wú)感,沒(méi)有任何心智負(fù)擔(dān)。
import { Constructor } from "vue/types/options";
export type WrapReadyProperty = T & { pageIsReady?: boolean }
/**
* 在@compontent 之后使用這個(gè)裝飾器,
* 組件就會(huì)被注入屬性 pageIsReady,
* 當(dāng)created和mounted都執(zhí)行完成時(shí) pageIsReady 變成true,
* 要求mounted或created是async/await。(取決于在哪個(gè)方法中發(fā)請(qǐng)求初始化組件)
* 然后可以在template中直接使用。
* 在script中使用調(diào)用isPageReady.call(this)方法;
*/
export default function PageReadyStatus() {
let createdDone = false;
let mountedDone = false;
function isCreatedMountedAllDone() {
return createdDone && mountedDone;
}
return function 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 = async function(...params: any[]) {
await oldCreated.apply(this, params);
createdDone = true;
this.pageIsReady = isCreatedMountedAllDone()
}
target.prototype.mounted = async function(...params: any[]) {
await oldMounted.apply(this, params);
mountedDone = true;
this.pageIsReady = isCreatedMountedAllDone()
}
target.prototype.beforeDestroy = async function(...params: any[]) {
await oldBeforeDestroy.apply(this, params);
mountedDone = false;
createdDone = false;
this.pageIsReady = false;
}
return target
};
}
export function isPageReady(this: WrapReadyProperty ) {
return this.pageIsReady
}
2. 給事件回調(diào)函數(shù)和按鈕Dom添加防抖與loading樣式
用于ts環(huán)境下的vue
通過(guò)裝飾器包裝被裝飾的方法。要求被包裝的方式是async/await的。這樣裝飾器內(nèi)只需要用一個(gè)await就可以得知被包裝的方法是否執(zhí)行完成。同時(shí),可以從事件對(duì)象中拿到被點(diǎn)擊的dom元素并修改它。
/*
* 請(qǐng)保證被包裝的方法的參數(shù)列表最后一個(gè)是點(diǎn)擊事件的參數(shù)
*/
export default function buttonThrottle() {
let pending = false;
return function(target: any, name: string): any {
const btnClickFunc = target[name];
const newFunc = async function(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;
};
}
用法: 在點(diǎn)擊事件函數(shù)上使用這個(gè)裝飾器。裝飾器會(huì)自動(dòng)檢測(cè)該函數(shù)是否執(zhí)行完成,并在執(zhí)行過(guò)程中往按鈕的Dom節(jié)點(diǎn)上添加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: {} })
export default class AddMemberInput extends Vue { @buttonThrottle()
private async confirmAdd() {
await this.addMembers(this.getVaildMembers());
}
}
3. mounted之前顯示白屏
用于js的vue中包裝vue的對(duì)象
同上,通過(guò)async/await獲得mounted或者created是否執(zhí)行完成 再通過(guò)指向vue實(shí)力的this拿到組件根節(jié)點(diǎn),然后按需修改它 以下代碼只是將組件隱藏了,實(shí)際上可以寫更復(fù)雜的邏輯,在加載過(guò)程中顯示其他內(nèi)容,畢竟拿到了Dom,想干嘛就干嘛。
function firstPaintControl(vueObj) {
let oldMounted = vueObj.mounted || function() {};
vueObj.mounted = async function(...params) {
this.$el.style.visibility = 'hidden';
await oldMounted.apply(this, params);
this.$el.style.visibility = 'visible';
};
return vueObj;
}
最后
如果你覺(jué)得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:
點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)
歡迎加我微信「qianyu443033099」拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...
關(guān)注公眾號(hào)「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。

