摘要:前言今天閑來(lái)時(shí)看了看中的新標(biāo)準(zhǔn)之一,裝飾器。過(guò)程中忽覺(jué)它和中的注解有一些類(lèi)似之處,并且當(dāng)前版本的中已經(jīng)支持它了,所以,就動(dòng)手在一個(gè)應(yīng)用中嘗鮮初體驗(yàn)了一番。另外,由于裝飾器目前還是中的一個(gè)提案,其中具體細(xì)節(jié)可能還會(huì)更改。
前言
今天閑來(lái)時(shí)看了看ES7中的新標(biāo)準(zhǔn)之一,裝飾器(Decorator)。過(guò)程中忽覺(jué)它和Java中的注解有一些類(lèi)似之處,并且當(dāng)前版本的TypeScript中已經(jīng)支持它了,所以,就動(dòng)手在一個(gè)Web應(yīng)用Demo中嘗鮮初體驗(yàn)了一番。
(裝飾器中文簡(jiǎn)介:這里)
最終效果:
// 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!" } }實(shí)現(xiàn) router裝飾器 包個(gè)外殼
由于我們需要一個(gè)集中存儲(chǔ)和掛載這些被裝飾的路由的地方。所以,我們先來(lái)給koa包個(gè)外殼:
// 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是我們存儲(chǔ)被修飾后的路由的地方,而registerRouters則是真實(shí)掛載它們的方法。
實(shí)現(xiàn)裝飾器現(xiàn)在實(shí)現(xiàn)下裝飾器,來(lái)把路由信息和處理函數(shù)保存起來(lái):
// 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]) } }
感覺(jué)TypeScript中的類(lèi)型已經(jīng)把代碼解釋得差不多了...
掛載最后實(shí)現(xiàn)一下把所有存起來(lái)的路由掛載上的方法,就大功告成了:
// 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!" } }
用起來(lái):
// app.ts "use strict" import Cover from "./Cover" export * from "./UserController" const app = new Cover() app.registerRouters() app.listen(3000)
寫(xiě)第三行代碼:export * from "./UserController" 的意圖為空?qǐng)?zhí)行一下該模塊內(nèi)的代碼(可否有更優(yōu)雅的辦法?)。
普通的koa中間件裝飾器普通的koa中間件裝飾器則更為簡(jiǎn)單,不需額外的存儲(chǔ)掛載過(guò)程,直接定義就好,以下為兩個(gè)簡(jiǎn)單的中間件裝飾器:
// 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!" } // ... }
一個(gè)需要注意的地方是,中間的經(jīng)過(guò)順序是由下至上的,故上面的例子中,會(huì)先進(jìn)入log中間件,然后是validateQuery。
最后以上例子僅是初體驗(yàn)時(shí)寫(xiě)的Demo代碼,部分地方可能略有粗糙。另外,由于裝飾器目前還是ES7中的一個(gè)提案,其中具體細(xì)節(jié)可能還會(huì)更改。個(gè)人感覺(jué)來(lái)說(shuō),它的確可以幫助代碼在某種程度上更為簡(jiǎn)潔清晰。不過(guò),由于它可以通過(guò)target參數(shù)直接取得被修飾類(lèi)本身,在TypeScript中可能還好,若在JavaScript里,如果大量混合使用各種第三方裝飾器,一個(gè)類(lèi)是否可能會(huì)被改的面目全非?最佳實(shí)踐可能還有待大家的一同探索。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/78540.html
摘要:裝飾者要實(shí)現(xiàn)這些相同的方法繼承自裝飾器對(duì)象創(chuàng)建具體的裝飾器,也是接收作對(duì)參數(shù)接下來(lái)我們要為每一個(gè)功能創(chuàng)建一個(gè)裝飾者對(duì)象,重寫(xiě)父級(jí)方法,添加我們想要的功能。 裝飾模式 僅僅包裝現(xiàn)有的模塊,使之 更加華麗 ,并不會(huì)影響原有接口的功能 —— 好比你給手機(jī)添加一個(gè)外殼罷了,并不影響手機(jī)原有的通話(huà)、充電等功能; 使用 ES7 的 decorator ES7 中增加了一個(gè) decorator 屬性...
摘要:下裝飾者的實(shí)現(xiàn)了解了裝飾者模式和的概念之后,我們寫(xiě)一段能夠兼容的代碼來(lái)實(shí)現(xiàn)裝飾者模式原函數(shù)拍照片定義函數(shù)裝飾函數(shù)加濾鏡用裝飾函數(shù)裝飾原函數(shù)這樣我們就實(shí)現(xiàn)了抽離拍照與濾鏡邏輯,如果以后需要自動(dòng)上傳功能,也可以通過(guò)函數(shù)來(lái)添加。 showImg(https://segmentfault.com/img/bVbueyz?w=852&h=356); 什么是裝飾者模式 當(dāng)我們拍了一張照片準(zhǔn)備發(fā)朋友...
摘要:裝飾器顧名思義就是裝飾某種東西的方法,可以用來(lái)裝飾屬性變量函數(shù)類(lèi)實(shí)例方法本質(zhì)上是個(gè)函數(shù)。以符開(kāi)頭,函數(shù)名稱(chēng)自擬。愛(ài)吃蘋(píng)果裝飾器裝飾類(lèi)愛(ài)吃蘋(píng)果結(jié)果是這個(gè)類(lèi)本身就可以通過(guò)修改類(lèi)的屬性增加屬性被裝飾的對(duì)象可以使用多個(gè)裝飾器。 @Decorator 裝飾器是es7的語(yǔ)法,這個(gè)方法對(duì)于面向切面編程有了更好的詮釋?zhuān)谝恍┣榫持锌梢允褂?,比如路人A的代碼實(shí)現(xiàn)了一需求,路人B希望用A的方法來(lái)實(shí)現(xiàn)一個(gè)新...
摘要:在學(xué)習(xí)裝飾器語(yǔ)法之前,需要先溫習(xí)一下的一些基礎(chǔ)知識(shí)。函數(shù)最后必須返回。使用時(shí)也很簡(jiǎn)單,如下在方法前面加上,就是裝飾器語(yǔ)法。裝備了,攻擊更強(qiáng)了。職業(yè)的基本攻擊穿上了,移動(dòng)速度更快了。 在學(xué)習(xí)ES7裝飾器語(yǔ)法之前,需要先溫習(xí)一下ES5的一些基礎(chǔ)知識(shí)。 假設(shè)有對(duì)象如下:(便于理解) var person = { name: TOM } 在ES5中,對(duì)象中的每個(gè)屬性都有一個(gè)特性值來(lái)描述...
閱讀 798·2023-04-26 03:04
閱讀 2873·2021-11-15 18:10
閱讀 1201·2021-09-03 10:28
閱讀 1138·2019-08-30 15:53
閱讀 899·2019-08-30 12:45
閱讀 1968·2019-08-30 11:03
閱讀 2871·2019-08-29 14:01
閱讀 2935·2019-08-28 18:24