索鸟网

  1. 首页
  2. 从 JavaScript 到 TypeScript 5 - 路由进化

从 JavaScript 到 TypeScript 5 - 路由进化


随着应用的庞大,项目中 JavaScript 的代码也会越来越臃肿,这时候许多 JavaScript 的语言弊端就会愈发明显,而 TypeScript 的出现,就是着力于解决 JavaScript 语言天生的弱势:静态类型。

前端开发 QQ 群:377786580

这篇文章首发于我的个人博客 《听说》,系列目录:

在上一篇文章 《从 JavaScript 到 TypeScript 4 - 装饰器和反射》 我们介绍了装饰器和反射,在这篇文章中,我们会把这两个特性引入,并且在 express 上,实现一层全新的路由封装。

express 路由

首先我们来看一个简单的 express 路由 (router):

// 对网站首页的访问返回 "Hello World!"
app.get("/", function (req: Request, res: Reponse) {
  res.send("Hello World!")
})


app.post("/user", function (req: Request, res: Reponse) {
  res.send(`User Id ${req.query.id}`)
})

在上面的路由代码我们演示了一个普通流水线式的路由。

基于上一篇文章中我们学到的装饰器和反射的知识,我们将要实现 路由的配置通过装饰器实现,并且实现一层路由逻辑的封装。

路由进化

基于装饰器和反射,我们要实现的路由最终效果是这样的:

class Home {
  @path("/user")
  @httpGet
  user (id: string) {
    return `User Id ${id}`
  }
}
GET  HTTP/1.1
Host: /user?id=tasaid.com

这段代码相比传统的路由配置,优点如下:

  • 将路由的配置抽离成为了装饰器,让整个 router 函数内部只需要处理业务逻辑即可,路由配置简单明了
  • 隐藏 reqres,每个 router 直接返回结果即可,无需自己再输出结果

装饰器: HTTP Method

我们先编写 HTTP Method 的装饰器,我们将实现两个装饰器,分别叫做 httpGethttpPost,对应 HTTP Method 的 GET/POST

原理上,我们会将 router 配置的数据都挂到使用装饰器的方法上。

import "reflect-metadata"

export const symbolHttpMethodsKey = Symbol("router:httpMethod")

export const httpGet = function (target: any, propertyKey: string) {
  // 挂载到调用装饰器的方法上
  Reflect.defineMetadata(symbolHttpMethodsKey, "get", target, propertyKey)
}

export const httpPost = function (target: any, propertyKey: string) {
  Reflect.defineMetadata(symbolHttpMethodsKey, "post", target, propertyKey)
}

装饰器: path

有了上面 HTTP Method 装饰器的实现,我们再实现 path 装饰器将会很简单。

当然,我们还可以在 path 中实现对原方法的封装:隐藏 reqres,并对 router 的输出结果进行封装。

注意这里使用的是装饰器工厂:

import "reflect-metadata"

export const symbolPathKey = Symbol.for("router:path")

export let path = (path: string): Function => {
  return function (target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<Function>) {

    Reflect.defineMetadata(symbolPathKey, path, target, propertyKey)

    if (!descriptor.value) return
    // 覆盖掉原来的 router method,在外层做封装
    let oldMethod = descriptor.value
    descriptor.value = function (req: Request, res: Response) {
      const params = Object.assign({}, req.body, req.query)
      let methodResult = oldMethod.call(this, params)
      // 输出返回结果
      res.send(methodResult)
    }
  }
}

Router? Controller!

现在,我们需要将所有的 Router 按照自己的业务规则/或者自定义的其他规则进行归类 —— 然后提取出对应的 Class,例如下面的 User Class 就是把用户信息所有的 router 都归类在一起:

class User {
  @httpPost
  @path("/user/login")
  login() { }

  @httpGet
  @path("/user/exit")
  exit() { }
}

然后在 express 配置的入口逻辑那里,把 class 对应的方法遍历一遍,然后使用 reflect-metadata 反射对应的 router 配置即可:

import "reflect-metadata"
// 装饰器挂载数据的 key
import { symbolHttpMethodsKey, symbolPathKey } from "./decorators"

const createController = (app: Express) => {
  let user = new User()
  for (let methodName in user) {
    let method = user[methodName]
    if (typeof method !== "function") break
    // 反射得到挂载的数据
    let httpMethod = Reflect.getMetadata(symbolHttpMethodsKey, user, methodName)
    let path = Reflect.getMetadata(symbolPathKey, user, methodName)

    // app.get("/", () => any)
    app[httpMethod](path, method)
  }
}

至此,我们的 express 路由进化完毕,效果如下:

完整的例子可以参考我的 Github

结语

装饰器目前在 ECMAScript 新提案中的 建议征集的第二阶段(Stage 2),由于装饰器在其他语言中早已实现,例如 Java 的注解(Annotation) 和 C# 的特性(Attribute),所以纳入 ECMAScript 规范只是时间问题了。

装饰器来装饰路由,并且封装 router 操作的的思路缘起 .NET MVC 架构:

angular 2.x 使用也引入了装饰器作为核心开发,随着规范的推进,相信装饰器进入大家视野,应用的场景也会越来越多。

在下一篇文章 《从 JavaScript 到 TypeScript 6 - Vue 引入 TypeScript》 中,我们将介绍如何在 Vue 中引入 TypeScript。

 

TypeScript 中文网:https://tslang.cn/

TypeScript 视频教程:《TypeScript 精通指南

typescript

来源地址:https://segmentfault.com/a/1190000011520859 版权归作者所有!

相关教程

  • 从 JavaScript 到 TypeScript

    本文首发在我的个人博客:http://muyunyun.cn/posts/66a5...文中的案例代码已经上传到 TypeScript TypeScript 并不是一个完全新的语言, 它是 JavaScript 的超集,为 JavaScript 的生态增加了类型机制,并最终将代码编译为纯粹的 JavaScript 代码。 TypeScript 简介 T
  • js进化,迁徙到typescript

    js进化,迁徙到typescript TypeScript 历史 TypeScript是一种由微软开发的自由和开源的编程语言 它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程 2012年十月份,微软发布了首个公开版本的TypeScript 当前最新版本v2.3.3 前言 js圈的,不管是前端还是no
  • 从 JavaScript 到 TypeScript - 接口

    前面讲 泛型 的时候,提到了接口。和泛型一样,接口也是目前 JavaScript 中并不存在的语法。 由于泛型语法总是附加在类或函数语法中,所以从 TypeScript 转译成 JavaScript 之后,至少还存在类和函数(只是去掉了泛型定义,类似 Java 泛型的类型擦除)。然而,如果在某个 .ts 文件中只定义了接口,转译后的 .js 文件将是一个
  • 从 JavaScript 到 TypeScript 6 - Vue 引入 TypeScript

    随着应用的庞大,项目中 JavaScript 的代码也会越来越臃肿,这时候许多 JavaScript 的语言弊端就会愈发明显,而 TypeScript 的出现,就是着力于解决 JavaScript 语言天生的弱势:静态类型。 前端开发 QQ 群:377786580 这篇文章首发于我的个人博客 《听说》,系列目录: 《从 JavaScript 到 Typ
  • 从 JavaScript 到 TypeScript - 声明类型

    从 JavaScript 语法改写为 TypeScript 语法,有两个关键点,一点是类成员变量(Field)需要声明,另一点是要为各种东西(变量、参数、函数/方法等)声明类型。而这两个点直接引出了两个关键性的问题,有哪些类型?怎样声明? 类型 在说 TypeScript 的类型之前,我们先复习一下 JavaScript 的七种类型: undefine
  • 从 JavaScript 到 TypeScript - 泛型

    TypeScript 为 JavaScriopt 带来了强类型特性,这就意味着限制了类型的自由度。同一段程序,为了适应不同的类型,就可能需要写不同的处理函数——而且这些处理函数中所有逻辑完全相同,唯一不同的就是类型——这严重违反抽象和复用代码的原则。 一个小实例 我们来模拟一个场景:某个服务提供了一些不同类型的数据,我们需要先通过一个中间件对这些数据进行
  • 从 JavaScript 到 TypeScript - 模块化和构建

    TypeScript 带来的最大好处就是静态类型检查,所以在从 JavaScript 转向 TypeScript 之前,一定要认识到添加类型定义会带来额外的工作量,这是必要的代价。不过,相对于静态类型检查带来的好处,这些代价是值得的。当然,TypeScript 允许不定义类型或者将所有类型定义为 any,但如果这样做,TypeScript 带来的大部分静
  • 从 JavaScript 到 TypeScript 3 - 引入和编译

    随着应用的庞大,项目中 JavaScript 的代码也会越来越臃肿,这时候许多 JavaScript 的语言弊端就会愈发明显,而 TypeScript 的出现,就是着力于解决 JavaScript 语言天生的弱势:静态类型。 前端开发 QQ 群:377786580 这篇文章首发于我的个人博客 《听说》,系列目录: 《从 JavaScript 到 Typ