成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

在 Web 應(yīng)用中使用 ES7 裝飾器(Decorator)初體驗(yàn)

ivan_qhz / 2870人閱讀

摘要:前言今天閑來(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

相關(guān)文章

  • ES7-Decorator-裝飾者模式

    摘要:裝飾者要實(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 屬性...

    zhangxiangliang 評(píng)論0 收藏0
  • 5 分鐘即可掌握的 JavaScript 裝飾者模式與 AOP

    摘要:下裝飾者的實(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ā)朋友...

    chunquedong 評(píng)論0 收藏0
  • es6/es7Decorator裝飾

    摘要:裝飾器顧名思義就是裝飾某種東西的方法,可以用來(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è)新...

    yanest 評(píng)論0 收藏0
  • MobX詳解(二):ES7 裝飾 decorator

    摘要:在學(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)描述...

    Keagan 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<