javascript
从 JavaScript 到 TypeScript 5 - 路由进化
隨著應用的龐大,項目中 JavaScript 的代碼也會越來越臃腫,這時候許多 JavaScript 的語言弊端就會愈發明顯,而 TypeScript 的出現,就是著力于解決 JavaScript 語言天生的弱勢:靜態類型。
前端開發 QQ 群:377786580
這篇文章首發于我的個人博客 《聽說》,系列目錄:
- 《從 JavaScript 到 TypeScript 1 - 什么是 TypeScript》
- 《從 JavaScript 到 TypeScript 2 - 基礎特性和類型推導》
- 《從 JavaScript 到 TypeScript 3 - 引入和編譯》
- 《從 JavaScript 到 TypeScript 4 - 裝飾器和反射》
- 《從 JavaScript 到 TypeScript 5 - express 路由進化》
- 《從 JavaScript 到 TypeScript 6 - vue 引入 TypeScript》
在上一篇文章 《從 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')@httpGetuser (id: string) {return `User Id ${id}`} } GET HTTP/1.1 Host: /user?id=tasaid.com這段代碼相比傳統的路由配置,優點如下:
- 將路由的配置抽離成為了裝飾器,讓整個 router 函數內部只需要處理業務邏輯即可,路由配置簡單明了
- 隱藏 req 和 res,每個 router 直接返回結果即可,無需自己再輸出結果
裝飾器: HTTP Method
我們先編寫 HTTP Method 的裝飾器,我們將實現兩個裝飾器,分別叫做 httpGet 和 httpPost,對應 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 中實現對原方法的封裝:隱藏 req 和 res,并對 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.valuedescriptor.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 精通指南》
總結
以上是生活随笔為你收集整理的从 JavaScript 到 TypeScript 5 - 路由进化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: XPath 详解,总结
- 下一篇: Select 子句后的别名,在where