關鍵詞 裝飾器
Decorator
超程式設計
前言
裝飾器是一種特殊型別的宣告,它能夠被附加到類宣告,方法, 訪問符,屬性或引數上。 裝飾器使用 @expression這種形式,expression求值後必須為一個函式,它會在執行時被呼叫,被裝飾的宣告資訊做為引數傳入。
本篇先從專案的巨集觀角度來總結一下Decorator如何組織。
我會持續分享一些知識整理,如果文章對您有幫助記得點贊鼓勵一下哦?~,也可以通過郵件方式聯絡我
文章列表: https://juejin.im/user/5bc8b9bf6fb9a05d1658bbbf/posts
郵箱地址: 595506910@qq.com
目錄
- 主要的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風格了。
vue-class-component
- @Component 如果您在宣告元件時更喜歡基於類的 API,則可以使用官方維護的 vue-class-component 裝飾器
- 實時計算computed屬性,
get computedMsg () {return 'computed ' + this.msg}
- 生命週期鉤子
mounted () {this.greet()}
vuex-class
讓Vuex和Vue之間的繫結更清晰和可擴充
- @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
- ts官網裝飾器說明【深入閱讀】 https://www.tslang.cn/docs/handbook/decorators.html
- Decorator作用在不同的宣告型別target、key說明 https://segmentfault.com/a/1190000010019412
- JS 裝飾器(Decorator)場景實戰 https://zhuanlan.zhihu.com/p/30487077
- Decorators in ES7 https://zhuanlan.zhihu.com/p/20139834
- vue元件 風格指南 https://cn.vuejs.org/v2/style-guide/index.html
- 使用Typescript封裝一款裝飾器風格的Web框架 https://zhuanlan.zhihu.com/p/33766385
- Axios 裝飾器實現 1.https://github.com/glangzh/retrofit-cjs 2.https://www.npmjs.com/package/axios-decorator
- 宣告計算屬性用的。 https://my.oschina.net/lpcysz/blog/2980469
- autobind https://blog.csdn.net/liubiggun/article/details/82147796
- vue-class-component原始碼閱讀 https://www.jianshu.com/p/cfed56d630a4
- 函式和方法的區別 https://www.imooc.com/article/19709?block_id=tuijian_wz
- 很多人整理過使用方法 https://www.cnblogs.com/navysummer/p/9689851.html 舉個例子 join(separator?: string): string;
- vue原始碼解析系列-compute實現機制 https://segmentfault.com/a/1190000009862528