摘要:前言今天閑來時看了看中的新標準之一,裝飾器。過程中忽覺它和中的注解有一些類似之處,并且當前版本的中已經支持它了,所以,就動手在一個應用中嘗鮮初體驗了一番。另外,由于裝飾器目前還是中的一個提案,其中具體細節可能還會更改。
前言
今天閑來時看了看ES7中的新標準之一,裝飾器(Decorator)。過程中忽覺它和Java中的注解有一些類似之處,并且當前版本的TypeScript中已經支持它了,所以,就動手在一個Web應用Demo中嘗鮮初體驗了一番。
(裝飾器中文簡介:這里)
最終效果:
// UserController.ts "use strict" import {router, log, validateQuery} from "./decorators" import {IContext} from "koa" export class UserController { @router({ method: "get", path: "/user/login" }) @validateQuery("username", "string") @log async login(ctx: IContext, next: Function) { ctx.body = "login!" } @router({ method: "get", path: "/user/logout" }) async logout(ctx: IContext, next: Function) { ctx.body = "logout!" } }實現 router裝飾器 包個外殼
由于我們需要一個集中存儲和掛載這些被裝飾的路由的地方。所以,我們先來給koa包個外殼:
// Cover.ts "use strict" import * as Koa from "koa" const router = require("koa-router")() class Cover { static __DecoratedRouters: Map<{target: any, method: string, path: string}, Function | Function[]> = new Map() private router: any private app: Koa constructor() { this.app = new Koa() this.router = router this.app.on("error", (err) => { if (err.status && err.status < 500) return console.error(err) }) } registerRouters() { // ... } listen(port: number) { this.app.listen(port) } } export default Cover
其中,__DecoratedRouters是我們存儲被修飾后的路由的地方,而registerRouters則是真實掛載它們的方法。
實現裝飾器現在實現下裝飾器,來把路由信息和處理函數保存起來:
// decorators.ts "use strict" import Cover from "./Cover" import {IContext} from "koa" export function router (config: {path: string, method: string}) { return (target: any, name: string, value: PropertyDescriptor) => { Cover.__DecoratedRouters({ target: target, path: config.path, method: config.method }, target[name]) } }
感覺TypeScript中的類型已經把代碼解釋得差不多了...
掛載最后實現一下把所有存起來的路由掛載上的方法,就大功告成了:
// Cover.ts "use strict" import * as Koa from "koa" const router = require("koa-router")() class Cover { // ... registerRouters() { for (let [config, controller] of Cover.__DecoratedRouters) { let controllers = Array.isArray(controller) ? controller : [controller] controllers.forEach((controller) => this.router[config.method](config.path, controller)) } this.app.use(this.router.routes()) this.app.use(this.router.allowedMethods()) } // ... } export default Cover // UserController.ts "use strict" import {router} from "./decorators" import {IContext} from "koa" export class UserController { @router({ method: "get", path: "/user/login" }) async login(ctx: IContext, next: Function) { ctx.body = "login!" } @router({ method: "get", path: "/user/logout" }) async logout(ctx: IContext, next: Function) { ctx.body = "logout!" } }
用起來:
// app.ts "use strict" import Cover from "./Cover" export * from "./UserController" const app = new Cover() app.registerRouters() app.listen(3000)
寫第三行代碼:export * from "./UserController" 的意圖為空執行一下該模塊內的代碼(可否有更優雅的辦法?)。
普通的koa中間件裝飾器普通的koa中間件裝飾器則更為簡單,不需額外的存儲掛載過程,直接定義就好,以下為兩個簡單的中間件裝飾器:
// decorators.ts "use strict" import Cover from "./Cover" import {IContext} from "koa" export function validateQuery (name, type) { return (target: any, name: string, value: PropertyDescriptor) => { if (!Array.isArray(target[name])) target[name] = [target[name]] target[name].splice(target[name].length - 1, 0, validate) } async function validate (ctx: IContext, next: Function) { if (typeof ctx.query[name] !== type) ctx.throw(400, `${name}"s type should be ${type}"`) await next() } } export function log (target: any, name: string, value: PropertyDescriptor) { if (!Array.isArray(target[name])) target[name] = [target[name]] target[name].splice(target[name].length - 1, 0, middleware) async function middleware (ctx: IContext, next: Function) { let start = Date.now() ctx.state.log = { path: ctx.path } try { await next() } catch (err) { if (err.status && err.status < 500) { Object.assign(ctx.state.log, { time: Date.now() - start, status: err.status, message: err.message }) console.log(ctx.state.log) } throw err } let onEnd = done.bind(ctx) ctx.res.once("finish", onEnd) ctx.res.once("close", onEnd) function done () { ctx.res.removeListener("finish", onEnd) ctx.res.removeListener("close", onEnd) Object.assign(ctx.state.log, { time: Date.now() - start, status: ctx.status }) console.log(ctx.state.log) } } }
裝飾上:
// UserController.ts "use strict" import {router, log, validateQuery} from "./decorators" import {IContext} from "koa" export class UserController { @router({ method: "get", path: "/user/login" }) @validateQuery("username", "string") @log async login(ctx: IContext, next: Function) { ctx.body = "login!" } // ... }
一個需要注意的地方是,中間的經過順序是由下至上的,故上面的例子中,會先進入log中間件,然后是validateQuery。
最后以上例子僅是初體驗時寫的Demo代碼,部分地方可能略有粗糙。另外,由于裝飾器目前還是ES7中的一個提案,其中具體細節可能還會更改。個人感覺來說,它的確可以幫助代碼在某種程度上更為簡潔清晰。不過,由于它可以通過target參數直接取得被修飾類本身,在TypeScript中可能還好,若在JavaScript里,如果大量混合使用各種第三方裝飾器,一個類是否可能會被改的面目全非?最佳實踐可能還有待大家的一同探索。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/78540.html
摘要:裝飾者要實現這些相同的方法繼承自裝飾器對象創建具體的裝飾器,也是接收作對參數接下來我們要為每一個功能創建一個裝飾者對象,重寫父級方法,添加我們想要的功能。 裝飾模式 僅僅包裝現有的模塊,使之 更加華麗 ,并不會影響原有接口的功能 —— 好比你給手機添加一個外殼罷了,并不影響手機原有的通話、充電等功能; 使用 ES7 的 decorator ES7 中增加了一個 decorator 屬性...
摘要:下裝飾者的實現了解了裝飾者模式和的概念之后,我們寫一段能夠兼容的代碼來實現裝飾者模式原函數拍照片定義函數裝飾函數加濾鏡用裝飾函數裝飾原函數這樣我們就實現了抽離拍照與濾鏡邏輯,如果以后需要自動上傳功能,也可以通過函數來添加。 showImg(https://segmentfault.com/img/bVbueyz?w=852&h=356); 什么是裝飾者模式 當我們拍了一張照片準備發朋友...
摘要:裝飾器顧名思義就是裝飾某種東西的方法,可以用來裝飾屬性變量函數類實例方法本質上是個函數。以符開頭,函數名稱自擬。愛吃蘋果裝飾器裝飾類愛吃蘋果結果是這個類本身就可以通過修改類的屬性增加屬性被裝飾的對象可以使用多個裝飾器。 @Decorator 裝飾器是es7的語法,這個方法對于面向切面編程有了更好的詮釋,在一些情境中可以使用,比如路人A的代碼實現了一需求,路人B希望用A的方法來實現一個新...
摘要:在學習裝飾器語法之前,需要先溫習一下的一些基礎知識。函數最后必須返回。使用時也很簡單,如下在方法前面加上,就是裝飾器語法。裝備了,攻擊更強了。職業的基本攻擊穿上了,移動速度更快了。 在學習ES7裝飾器語法之前,需要先溫習一下ES5的一些基礎知識。 假設有對象如下:(便于理解) var person = { name: TOM } 在ES5中,對象中的每個屬性都有一個特性值來描述...
閱讀 790·2023-04-26 03:04
閱讀 2866·2021-11-15 18:10
閱讀 1195·2021-09-03 10:28
閱讀 1134·2019-08-30 15:53
閱讀 889·2019-08-30 12:45
閱讀 1959·2019-08-30 11:03
閱讀 2868·2019-08-29 14:01
閱讀 2931·2019-08-28 18:24