天天看點

活用async/await,實作一些讓Vue更好用的裝飾器給vue添加一個訓示初始化完成的變量。給事件回調函數和按鈕Dom添加防抖與loading樣式注入loading變量mounted之前顯示白屏

點選上方 前端瓶子君,關注公衆号

回複算法,加入前端程式設計面試算法每日一題群

活用async/await,實作一些讓Vue更好用的裝飾器給vue添加一個訓示初始化完成的變量。給事件回調函數和按鈕Dom添加防抖與loading樣式注入loading變量mounted之前顯示白屏

Async/await加上裝飾器,會産生神奇的效果。

以下所列幾個裝飾器,都要求被裝飾的方法寫成async/await,

這樣就可以利用async/await本身的特性,從方法外部得知異步方法是否運作完成。

于是便實作了将繁雜和麻煩的邏輯隐藏在裝飾器内部,使業務代碼更加幹淨。

以下裝飾器除了最後一個都是用在Typescript環境下的class寫法的vue裡的。

最後一個其實并不是裝飾器,而是一個高階函數,但是衆所周知,裝飾器本質上就是個高階函數。

是以所有裝飾器都可以很容易的改成高階函數,然後用在js環境的vue裡。

給vue添加一個訓示初始化完成的變量。

  1. 使用場景舉例:

    搜尋頁面:搜尋中顯示loading,結果為空時顯示暫無資料。

    第一次打開頁面時,在created或者mounted中送出請求,進行初始化的搜尋,此時搜尋還沒完成,顯示暫無資料并不合适。

    這個時候就需要一個變量來判斷頁面是不是第一次打開。

  2. 代碼解釋:

    通過裝飾器添加這個屬性(pageIsReady)到元件的類上,

    并包裝vue的created, mounted和beforeDestroy方法,監控元件的生命周期。

    當created或者mounted裡發出的請求完成後,就把pageIsReady設為true。

    然後在beforeDestroy中重置狀态,因為裝飾器用了閉包,隻會執行個體化一次。

import { Constructor } from "vue/types/options";
export type WrapReadyProperty<T> = T & {
    pageIsReady?: boolean;
    createdDone?: boolean;
    mountedDone?: boolean
}
/**  
 * 在@compontent 之後使用這個裝飾器,
 * 元件就會被注入屬性 pageIsReady,
 * 當created和mounted都執行完成時 pageIsReady 變成true,
 * 要求mounted或created是async/await。(取決于在哪個方法中發請求初始化元件)
 * 然後可以在template中直接使用。
 * 在script中使用調用isPageReady.call(this)方法;
    */
export default function PageReadyStatus() {
    return function pageReadyEnhancement<T extends WrapReadyProperty<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;
        function isCreatedMountedAllDone(this: T) {
            return !!this.createdDone && !!this.mountedDone;
        }
        target.prototype.created = async function(...params: any[]) {
            await oldCreated.apply(this, params);
            this.createdDone = true;
            this.pageIsReady = isCreatedMountedAllDone.call(this)
        }
        target.prototype.mounted = async function(...params: any[]) {
            await oldMounted.apply(this, params);
            this.mountedDone = true
            this.pageIsReady = isCreatedMountedAllDone.call(this)
        }
        target.prototype.beforeDestroy = async function(...params: any[]) {
            await oldBeforeDestroy.apply(this, params);
            this.createdDone = false;
            this.mountedDone = true
            this.pageIsReady = false;
        }
        return target
    };
}

export function isPageReady(this: WrapReadyProperty<Vue>) {
    return this.pageIsReady
} 
           

給事件回調函數和按鈕Dom添加防抖與loading樣式

  1. 使用場景舉例:

    點選一個按鈕,觸發一個函數,并發出一個請求。此時需要給這個函數防抖,并給按鈕增加一個loading的樣式

  2. 代碼解釋

    利用async/await把異步變成同步的特性,在裝飾器中發現方法還沒執行完成時直接傳回。

    并從時間對象中拿到按鈕的dom節點,改變按鈕的樣式。我這裡是用了一個cursor:wait;

/*
 * 請保證被包裝的方法的參數清單最後一個是點選事件的參數
 */
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;
    };
} 
           
  1. 用法:

    在點選事件函數上使用這個裝飾器.

 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());
        }    
    }
           

注入loading變量

  1. 場景

    發請求時在頁面上加一個loading的狀态,比如element-ui裡就有一個v-loading的指令。

    這個指令需要一個是否在loading中的變量

  2. 代碼解釋

    往元件上增加一個變量,變量名由參數variableName指定

    該變量會在被包裝的方法執行期間為true

    這樣就不用自己寫this.loading=true this.loading=false了

 export default function FunctionLoadingVariable(variableName: string) {
       return function(target: any, name: string): any {
           target[variableName] = false;
           const btnClickFunc = target[name];
           const newFunc = async function(this: Vue & {
               [key: string]: boolean
           }, ...params: any[]) {
               try {
                   this[variableName] = true
                   await btnClickFunc.apply(this, params);
               } catch (error) {
                   console.error(error);
               }
               this[variableName] = false
           };
           target[name] = newFunc;
           return target;
       };
   } 
           

mounted之前顯示白屏

  1. 場景

    頁面加載的過程很醜,是以可以在mouted之前顯示白屏,或者弄一個骨架屏。

    特别是在微信小程式中,因為加載完成之前特别醜,是以幾乎所有小程式都會在mounted之前白屏。

  2. 通過async/await獲得mounted或者created是否執行完成

    再通過指向vue實力的this拿到元件根節點,然後按需修改它

    以下代碼隻是将元件隐藏了,實際上可以寫更複雜的邏輯,在加載過程中顯示其他内容,畢竟拿到了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;
  }
           

關于本文

來源:OLong

https://segmentfault.com/a/1190000037604556

最後

歡迎關注【前端瓶子君】✿✿ヽ(°▽°)ノ✿

回複「算法」,加入前端程式設計源碼算法群,每日一道面試題(工作日),第二天瓶子君都會很認真的解答喲!

回複「交流」,吹吹水、聊聊技術、吐吐槽!

回複「閱讀」,每日刷刷高品質好文!

如果這篇文章對你有幫助,「在看」是最大的支援

 》》面試官也在看的算法資料《《

“在看和轉發”就是最大的支援