Vue框架TypeScript裝飾器使用指南

關鍵詞 裝飾器 Decorator 元編程html

前言

裝飾器是一種特殊類型的聲明,它可以被附加到類聲明,方法, 訪問符,屬性或參數上。 裝飾器使用 @expression這種形式,expression求值後必須爲一個函數,它會在運行時被調用,被裝飾的聲明信息作爲參數傳入。vue

爲何要用裝飾器?

拷問靈魂的問題! 由於裝飾器能夠 對業務代碼非侵入ios

本篇先從項目的宏觀角度來總結一下Decorator如何組織。git

我會持續分享一些知識整理,若是文章對您有幫助記得點贊鼓勵一下哦😁~,也能夠經過郵件方式聯繫我github

文章列表: juejin.im/user/5bc8b9…vuex

郵箱地址: 595506910@qq.comvue-cli

目錄

  • 主要的Decorator依賴
    • vue-class-component
    • vuex-class
    • vue-property-decorator
    • core-decorators
  • 自定義Decorator示例
  • 哪些功能適合用Decorator實現
  • Decorator實現小Tips
  • See also

主要的Decorator依賴

vue-cli3 默認支持Decorator, 年初重寫了一個design庫主要依賴官方和社區提供的Decorator來實現的組件。 Decorator能夠非侵入的裝飾類、方法、屬性,解耦業務邏輯和輔助功能邏輯。如下是主要的三方Decorator組件,有了這些組件經常使用的Vue特性就能夠所有轉成Decorator風格了。express

vue-class-component

  • @Component 若是您在聲明組件時更喜歡基於類的 API,則可使用官方維護的 vue-class-component 裝飾器
  • 實時計算computed屬性, get computedMsg () {return 'computed ' + this.msg}
  • 生命週期鉤子 mounted () {this.greet()}

vuex-class

讓Vuex和Vue之間的綁定更清晰和可拓展npm

  • @State
  • @Getter
  • @Action
  • @Mutation

vue-property-decorator

這個組件徹底依賴於vue-class-component.而且參考vuex-class組件,它具有如下幾個屬性:編程

  • @Component (徹底繼承於vue-class-component)
  • @Prop:父子組件之間值的傳遞
  • @Emit:子組件向父組件傳遞
  • @Model:雙向綁定
  • @Watch:觀察的表達式變更
  • @Provide:在組件嵌套層級過深時。父組件不便於向子組件傳遞數據。就把數據經過Provide傳遞下去。
  • @Inject:而後子組件經過Inject來獲取
  • Mixins (在vue-class-component中定義);

建議項目中只引用vue-property-decorator就能夠了,避免@Component從vue-class-component和vue-property-decorator兩個中隨意引用。

core-decorators

  • @readonly
  • @autobind : TSX 回調函數中的 this,類的方法默認是不會綁定 this 的,可使用autobind裝飾器
  • @override

總結一下主要就分紅這三類:

  • 修飾類的:@Component、@autobind;
  • 修飾方法的:@Emit、@Watch、@readonly、@override;
  • 修飾屬性的:@Prop、@readonly;

以上引用方法等詳系內容可查看官方文檔。要想完整的發揮Decorator的價值就須要根據須要自定義一些裝飾器。下面自定義部分就來實現一個記錄日誌功能的裝飾器。

約定優於配置

props: {
    name: {
        type: string,
        defalut: ''
    }
}

vs

@Prop name:string
複製代碼

傳統的寫法就是配置式的聲明,下面這個優雅多了,類型和做用一目瞭然。

自定義Decorator示例

  • @Logger,1.Logger日誌裝飾器一般是修飾方法,Decorater則是在運行時就被觸發了,日誌記錄是在方法被調用時觸發,示例中經過自動發佈事件實現調用時觸發。2.爲增長日誌記錄的靈活性,須要經過暴露鉤子函數的方式來改變日誌記錄的內容。

指望的日誌格式

{
    "logId":"", // 事件Id
    "input":"", // 方法輸入的內容
    "output":"", // 方法輸出的內容
    "custom":"" // 自定義的日誌內容
}
複製代碼

實現

export function Logger(logId?: string, hander?: Function) {
    const loggerInfo =Object.seal({logId:logId, input:'',output:'', custom: ''});
    const channelName = '__logger';
    const msgChannel = postal.channel(channelName);
    msgChannel.subscribe(logId, logData => {
        // 根據業務邏輯來處理日誌
        console.log(logData);
    });

    return function (target: any, key: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> {
            const oldValue = descriptor.value
            descriptor.value = function () {
                const args: Array<any> = [];
                for (let index in arguments) {
                args.push(arguments[index]);
                }
                loggerInfo.input = `${key}(${args.join(',')})`;
                // 執行原方法
                const value = oldValue.apply(this, arguments);
                loggerInfo.output = value;
                hander && (loggerInfo.custom = hander(loggerInfo.input, loggerInfo.output) || '');
                // 被調用時,會自動發出一個事件
                msgChannel.publish(logId, loggerInfo);
            }
            return descriptor
    }
}
複製代碼

使用

// 直接使用很是簡潔
@Logger('event_get_detial1')
getDetial(id?: string, category?: string) {
    return "詳細內容";
}
// 或者使用自定義,讓日誌和業務邏輯分離
@Logger('event_get_detial2', (input, output) => {
        return '我是自定義內容';
})
getDetial2(id?: string, category?: string) {
    return "詳細內容";
}
...
<button @click="getDetial2('1000', 'a')">獲取詳情</button>
複製代碼

效果: {logId: "event_get_detial2", input: "getDetial2(1000,a)", output: "詳細內容", custom: "我是自定義內容"}, 每次點擊按鈕都會觸發一次。

TODO: 這裏還須要對輸入參數和輸出參數中的引用數據類型作處理。

同時還須要掌握:裝飾器工廠、裝飾器組合、裝飾器求值、參數裝飾器、元數據

哪些功能適合用Decorator實現

  • 官網和社區提供的這些Decorator, 能夠做爲本身框架的底層設計。
  • 日誌功能全局都得用,調用方法基本一致,是最適合使用裝飾器來實現,而且每一個項目的日誌記錄各有差別,最適合自定義這部分。

Decorator實現小Tips

  • 考慮下各種Decorator疊加和共存的問題,能夠參考官網關於裝飾器組合描述

  • Decorator 的目標是在原有功能基礎上,添加功能,切忌覆蓋原有功能

  • 類裝飾器不能用在聲明文件中( .d.ts),也不能用在任何外部上下文中(好比declare的類)

  • 裝飾器只能用於類和類的方法,不能用於函數,由於存在函數提高。類是不會提高的,因此就沒有這方面的問題。

  • 注意遷移速度、避免一口吃成胖子的作法

  • 不要另起爐竈對主流庫建立Decorator庫,主流庫維護成本很高仍是得有官方來維護,爲保證質量不使用我的編寫的Decorator庫。本身在建立Decorator庫時也要有這個意識,僅作一些有必要自定義的。

  • Decorator 不是管道模式,decorator之間不存在交互,因此必須注意保持decorator獨立性、透明性

  • Decorator 更適用於非業務功能需求

  • 肯定 decorator 的用途後,切記執行判斷參數類型

  • decorator 針對每一個裝飾目標,僅執行一次

See also

相關文章
相關標籤/搜索