自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何讓你的 Express 飛起來

開發(fā)
本文阿寶哥將以 Github 上的 OvernightJS 開源項(xiàng)目為例,來介紹一下如何使用 TypeScript 裝飾器來裝飾 Express,從而讓你的 Express 好用得飛起來。

接下來本文的重心將圍繞 裝飾器 的應(yīng)用展開,不過在分析裝飾器在 OvernightJS 的應(yīng)用之前,阿寶哥先來簡單介紹一下 OvernightJS。

一、OvernightJS 簡介 

  1. TypeScript decorators for the ExpressJS Server. 

OvernightJS 是一個(gè)簡單的庫,用于為要調(diào)用 Express 路由的方法添加 TypeScript 裝飾器。此外,該項(xiàng)目還包含了用于管理 json-web-token 和打印日志的包。

[[344477]]

1.1 OvernightJS 特性
OvernightJS 并不是為了替代 Express,如果你之前已經(jīng)掌握了 Express,那你就可以快速地學(xué)會(huì)它。OvernightJS 為開發(fā)者提供了以下特性:

  • 使用 @Controller 裝飾器定義基礎(chǔ)路由;
  • 提供了把類方法轉(zhuǎn)化為 Express 路由的裝飾器(比如 @Get,@Put,@Post,@Delete);
  • 提供了用于處理中間件的 @Middleware 和 @ClassMiddleware 裝飾器;
  • 提供了用于處理異常的 @ErrorMiddleware 裝飾器;
  • 提供了 @Wrapper 和 @ClassWrapper 裝飾器用于包裝函數(shù);
  • 通過 @ChildControllers 裝飾器支持子控制器。

出于篇幅考慮,阿寶哥只介紹了 OvernightJS 與裝飾器相關(guān)的部分特性。了解完這些特性,我們來快速體驗(yàn)一下 OvernightJS。

1.2 OvernightJS 入門
1.2.1 初始化項(xiàng)目
首先新建一個(gè) overnight-quickstart 項(xiàng)目,然后使用 npm init -y 命令初始化項(xiàng)目,然后在命令行中輸入以下命令來安裝項(xiàng)目依賴包:

  1. $ npm i @overnightjs/core express -S 

在 Express 項(xiàng)目中要集成 TypeScript 很簡單,只需安裝 typescript 這個(gè)包就可以了。但為了在開發(fā)階段能夠在命令行直接運(yùn)行使用 TypeScript 開發(fā)的服務(wù)器,我們還需要安裝 ts-node 這個(gè)包。要安裝這兩個(gè)包,我們只需在命令行中輸入以下命令:

  1. $ npm i typescript ts-node -D 

1.2.2 為 Node.js 和 Express 安裝聲明文件
聲明文件是預(yù)定義的模塊,用于告訴 TypeScript 編譯器的 JavaScript 值的形狀。類型聲明通常包含在擴(kuò)展名為 .d.ts 的文件中。這些聲明文件可用于所有最初用 JavaScript 而非 TypeScript 編寫的庫。

幸運(yùn)的是,我們不需要重頭開始為 Node.js 和 Express 定義聲明文件,因?yàn)樵?Github 上有一個(gè)名為 DefinitelyTyped 項(xiàng)目已經(jīng)為我們提供了現(xiàn)成的聲明文件。

要安裝 Node.js 和 Express 對(duì)應(yīng)的聲明文件,我們只需要在命令行執(zhí)行以下命令就可以了:

  1. $ npm i @types/node @types/express -D 

該命令成功執(zhí)行之后,package.json 中的 devDependencies 屬性就會(huì)新增 Node.js 和 Express 對(duì)應(yīng)的依賴包版本信息:

  1.   "devDependencies": { 
  2.      "@types/express""^4.17.8"
  3.      "@types/node""^14.11.2"
  4.      "ts-node""^9.0.0"
  5.      "typescript""^4.0.3" 
  6.   } 

1.2.3 初始化 TypeScript 配置文件
為了能夠靈活地配置 TypeScript 項(xiàng)目,我們還需要為本項(xiàng)目生成 TypeScript 配置文件,在命令行輸入 tsc --init 之后,項(xiàng)目中就會(huì)自動(dòng)創(chuàng)建一個(gè) tsconfig.json 的文件。對(duì)于本項(xiàng)目來說,我們將使用以下配置項(xiàng):

  1.   "compilerOptions": { 
  2.     "target""es6"
  3.     "module""commonjs"
  4.     "rootDir""./src"
  5.     "outDir""./build"
  6.     "esModuleInterop"true
  7.     "experimentalDecorators"true
  8.     "strict"true 
  9.   } 

1.2.4 創(chuàng)建簡單的 Web 服務(wù)器
在創(chuàng)建簡單的 Web 服務(wù)器之前,我們先來初始化項(xiàng)目的目錄結(jié)構(gòu)。首先在項(xiàng)目的根目錄下創(chuàng)建一個(gè) src 目錄及 controllers 子目錄:

  1. ├── src 
  2. │   ├── controllers 
  3. │   │   └── UserController.ts 
  4. │   └── index.ts 

接著新建 UserController.ts 和 index.ts 這兩個(gè)文件并分別輸入以下內(nèi)容:

UserController.ts

  1. import { Controller, Get } from "@overnightjs/core"
  2. import { Request, Response } from "express"
  3.  
  4. @Controller("api/users"
  5. export class UserController { 
  6.   @Get(""
  7.   private getAll(req: Request, res: Response) { 
  8.     return res.status(200).json({ 
  9.       message: "成功獲取所有用戶"
  10.     }); 
  11.   } 

index.ts

  1. import { Server } from "@overnightjs/core"
  2. import { UserController } from "./controllers/UserController"
  3.  
  4. const PORT = 3000; 
  5.  
  6. export class SampleServer extends Server { 
  7.   constructor() { 
  8.     super(process.env.NODE_ENV === "development"); 
  9.     this.setupControllers(); 
  10.   } 
  11.  
  12.   private setupControllers(): void { 
  13.     const userController = new UserController(); 
  14.     super.addControllers([userController]); 
  15.   } 
  16.  
  17.   public start(port: number): void { 
  18.     this.app.listen(port, () => { 
  19.       console.log(`⚡️[server]: Server is running at http://localhost:${PORT}`); 
  20.     }); 
  21.   } 
  22.  
  23. const sampleServer = new SampleServer(); 
  24. sampleServer.start(PORT); 

完成上述步驟之后,我們?cè)陧?xiàng)目的 package.json 中添加一個(gè) start 命令來啟動(dòng)項(xiàng)目:

  1.   "scripts": { 
  2.     "start""ts-node ./src/index.ts" 
  3.   }, 

添加完 start 命令,我們就可以在命令行中通過 npm start 來啟動(dòng) Web 服務(wù)器了。當(dāng)服務(wù)器成功啟動(dòng)之后,命令行會(huì)輸出以下消息:

  1. > ts-node ./src/index.ts 
  2.  
  3. ⚡️[server]: Server is running at http://localhost:3000 

接著我們打開瀏覽器訪問 http://localhost:3000/api/users 這個(gè)地址,你就會(huì)看到 {"message":"成功獲取所有用戶"} 這個(gè)信息。

1.2.5 安裝 nodemon
為了方便后續(xù)的開發(fā),我們還需要安裝一個(gè)第三方包 nodemon。對(duì)于寫過 Node.js 應(yīng)用的小伙伴來說,對(duì) nodemon 這個(gè)包應(yīng)該不會(huì)陌生。nodemon 這個(gè)包會(huì)自動(dòng)檢測目錄中文件的更改,當(dāng)發(fā)現(xiàn)文件異動(dòng)時(shí),會(huì)自動(dòng)重啟 Node.js 應(yīng)用程序。

同樣,我們?cè)诿钚袌?zhí)行以下命令來安裝它:

  1. $ npm i nodemon -D 

安裝完成后,我們需要更新一下前面已經(jīng)創(chuàng)建的 start 命令:

  1.   "scripts": { 
  2.     "start""nodemon ./src/index.ts" 
  3.   } 

好的,現(xiàn)在我們已經(jīng)知道如何使用 OvernightJS 來開發(fā)一個(gè)簡單的 Web 服務(wù)器。接下來,阿寶哥將帶大家一起來分析 OvernightJS 是如何使用 TypeScript 裝飾器實(shí)現(xiàn)上述的功能。

二、OvernightJS 原理分析
在分析前面示例中 @Controller 和 @Get 裝飾器原理前,我們先來看一下直接使用 Express 如何實(shí)現(xiàn)同樣的功能:

  1. import express, { Router, Request, Response } from "express"
  2. const app = express(); 
  3.  
  4. const PORT = 3000; 
  5. class UserController { 
  6.   public getAll(req: Request, res: Response) { 
  7.     return res.status(200).json({ 
  8.       message: "成功獲取所有用戶"
  9.     }); 
  10.   } 
  11.  
  12. const userRouter = Router(); 
  13. const userCtrl = new UserController(); 
  14. userRouter.get("/", userCtrl.getAll); 
  15.  
  16. app.use("/api/users", userRouter); 
  17.  
  18. app.listen(PORT, () => { 
  19.   console.log(`⚡️[server]: Server is running at http://localhost:${PORT}`); 
  20. }); 

在以上代碼中,我們先通過調(diào)用 Router 方法創(chuàng)建了一個(gè) userRouter 對(duì)象,然后進(jìn)行相關(guān)路由的配置,接著使用 app.use 方法應(yīng)用 userRouter 路由。下面我們用一張圖來直觀感受一下 OvernightJS 與 Express 在使用上的差異:

通過以上對(duì)比可知,利用 OvernightJS 提供的裝飾器,可以讓我們開發(fā)起來更加便捷。但大家要記住 OvernightJS 底層還是基于 Express,其內(nèi)部最終還是通過 Express 提供的 API 來處理路由。

接下來為了能更好理解后續(xù)的內(nèi)容,我們先來簡單回顧一下 TypeScript 裝飾器。

2.1 TypeScript 裝飾器簡介
裝飾器是一個(gè)表達(dá)式,該表達(dá)式執(zhí)行后,會(huì)返回一個(gè)函數(shù)。在 TypeScript 中裝飾器可以分為以下 4 類:

需要注意的是,若要啟用實(shí)驗(yàn)性的裝飾器特性,你必須在命令行或 tsconfig.json 里啟用 experimentalDecorators 編譯器選項(xiàng):

命令行:

  1. tsc --target ES5 --experimentalDecorators 

tsconfig.json:

  1.   "compilerOptions": { 
  2.      "experimentalDecorators"true 
  3.    } 

了解完 TypeScript 裝飾器的分類,我們來開始分析 OvernightJS 框架中提供的裝飾器。

2.2 @Controller 裝飾器
在前面創(chuàng)建的簡單 Web 服務(wù)器中,我們通過以下方式來使用 @Controller 裝飾器:

  1. @Controller("api/users"
  2. export class UserController {} 

很明顯該裝飾器應(yīng)用在 UserController 類上,它屬于類裝飾器。OvernightJS 的項(xiàng)目結(jié)構(gòu)很簡單,我們可以很容易找到 @Controller 裝飾器的定義:

  1. // src/core/lib/decorators/class.ts 
  2. export function Controller(path: string): ClassDecorator { 
  3.   return <TFunction extends Function>(target: TFunction): void => { 
  4.     addBasePathToClassMetadata(target.prototype, "/" + path); 
  5.   }; 

通過觀察以上代碼可知,Controller 函數(shù)是一個(gè)裝飾器工廠,即調(diào)用該工廠方法之后會(huì)返回一個(gè) ClassDecorator 對(duì)象。在 ClassDecorator 內(nèi)部,會(huì)繼續(xù)調(diào)用 addBasePathToClassMetadata 方法,把基礎(chǔ)路徑添加到類的元數(shù)據(jù)中:

  1. // src/core/lib/decorators/class.ts 
  2. export function addBasePathToClassMetadata(target: Object, basePath: string): void { 
  3.   let metadata: IClassMetadata | undefined = Reflect.getOwnMetadata(classMetadataKey, target); 
  4.   if (!metadata) { 
  5.       metadata = {}; 
  6.   } 
  7.   metadata.basePath = basePath; 
  8.   Reflect.defineMetadata(classMetadataKey, metadata, target); 

addBasePathToClassMetadata 函數(shù)的實(shí)現(xiàn)很簡單,主要是利用 Reflect API 實(shí)現(xiàn)元數(shù)據(jù)的存取操作。在以上代碼中,會(huì)先獲取 target 對(duì)象上已保存的 metadata 對(duì)象,如果不存在的話,會(huì)創(chuàng)建一個(gè)空的對(duì)象,然后把參數(shù) basePath 的值添加該對(duì)象的 basePath 屬性中,元數(shù)據(jù)設(shè)置完成后,在通過 Reflect.defineMetadata 方法進(jìn)行元數(shù)據(jù)的保存。

下面我們用一張圖來說明一下 @Controller 裝飾器的處理流程:

在 OvernightJS 項(xiàng)目中,所使用的 Reflect API 是來自 reflect-metadata 這個(gè)第三方庫。該庫提供了很多 API 用于操作元數(shù)據(jù),這里我們只簡單介紹幾個(gè)常用的 API:

  1. // define metadata on an object or property 
  2. Reflect.defineMetadata(metadataKey, metadataValue, target); 
  3. Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey); 
  4.  
  5. // check for presence of a metadata key on the prototype chain of an object or property 
  6. let result = Reflect.hasMetadata(metadataKey, target); 
  7. let result = Reflect.hasMetadata(metadataKey, target, propertyKey); 
  8.  
  9. // get metadata value of an own metadata key of an object or property 
  10. let result = Reflect.getOwnMetadata(metadataKey, target); 
  11. let result = Reflect.getOwnMetadata(metadataKey, target, propertyKey); 
  12.  
  13. // get metadata value of a metadata key on the prototype chain of an object or property 
  14. let result = Reflect.getMetadata(metadataKey, target); 
  15. let result = Reflect.getMetadata(metadataKey, target, propertyKey); 
  16.  
  17. // delete metadata from an object or property 
  18. let result = Reflect.deleteMetadata(metadataKey, target); 
  19. let result = Reflect.deleteMetadata(metadataKey, target, propertyKey); 

相信看到這里,可能有一些小伙伴會(huì)有疑問,通過 Reflect API 保存的元數(shù)據(jù)什么時(shí)候使用呢?這里我們先記住這個(gè)問題,后面我們?cè)賮矸治鏊?,接下來我們來開始分析 @Get 裝飾器。

2.3 @Get 裝飾器
在前面創(chuàng)建的簡單 Web 服務(wù)器中,我們通過以下方式來使用 @Get 裝飾器,該裝飾器用于配置 Get 請(qǐng)求:

  1. export class UserController { 
  2.   @Get(""
  3.   private getAll(req: Request, res: Response) { 
  4.     return res.status(200).json({ 
  5.       message: "成功獲取所有用戶"
  6.     }); 
  7.   } 

@Get 裝飾器應(yīng)用在 UserController 類的 getAll 方法上,它屬于方法裝飾器。它的定義如下所示:

  1. // src/core/lib/decorators/method.ts 
  2. export function Get(path?: string | RegExp): MethodDecorator & PropertyDecorator { 
  3.   return helperForRoutes(HttpVerb.GET, path); 

與 Controller 函數(shù)一樣,Get 函數(shù)也是一個(gè)裝飾器工廠,調(diào)用該函數(shù)之后會(huì)返回 MethodDecorator & PropertyDecorator 的交叉類型。除了 Get 請(qǐng)求方法之外,常見的 HTTP 請(qǐng)求方法還有 Post、Delete、Put、Patch 和 Head 等。為了統(tǒng)一處理這些請(qǐng)求方法,OvernightJS 內(nèi)部封裝了一個(gè) helperForRoutes 函數(shù),該函數(shù)的具體實(shí)現(xiàn)如下:

  1. // src/core/lib/decorators/method.ts 
  2. function helperForRoutes(httpVerb: HttpDecorator, path?: string | RegExp): MethodDecorator & PropertyDecorator { 
  3.   return (target: Object, propertyKey: string | symbol): void => { 
  4.       let newPath: string | RegExp; 
  5.       if (path === undefined) { 
  6.           newPath = ''
  7.       } else if (path instanceof RegExp) { 
  8.           newPath = addForwardSlashToFrontOfRegex(path); 
  9.       } else { // assert (path instanceof string) 
  10.           newPath = '/' + path; 
  11.       } 
  12.       addHttpVerbToMethodMetadata(target, propertyKey, httpVerb, newPath); 
  13.     }; 

觀察以上代碼可知,在 helperForRoutes 方法內(nèi)部,會(huì)繼續(xù)調(diào)用 addHttpVerbToMethodMetadata 方法把請(qǐng)求方法和請(qǐng)求路徑這些元數(shù)據(jù)保存起來。

  1. // src/core/lib/decorators/method.ts 
  2. export function addHttpVerbToMethodMetadata(target: Object, metadataKey: any,  
  3.   httpDecorator: HttpDecorator, path: string | RegExp): void { 
  4.     let metadata: IMethodMetadata | undefined = Reflect.getOwnMetadata(metadataKey, target); 
  5.     if (!metadata) { 
  6.         metadata = {}; 
  7.     } 
  8.     if (!metadata.httpRoutes) { 
  9.         metadata.httpRoutes = []; 
  10.     } 
  11.     const newArr: IHttpRoute[] = [{ 
  12.       httpDecorator, 
  13.       path, 
  14.     }]; 
  15.     newArr.push(...metadata.httpRoutes); 
  16.     metadata.httpRoutes = newArr; 
  17.     Reflect.defineMetadata(metadataKey, metadata, target); 

在 addHttpVerbToMethodMetadata 方法中,會(huì)先獲取已保存的元數(shù)據(jù),如果 metadata 對(duì)象不存在則會(huì)創(chuàng)建一個(gè)空的對(duì)象。然后會(huì)繼續(xù)判斷該對(duì)象上是否含有 httpRoutes 屬性,沒有的話會(huì)使用 [] 對(duì)象來作為該屬性的屬性值。而請(qǐng)求方法和請(qǐng)求路徑這些元數(shù)據(jù)會(huì)以對(duì)象的形式保存到數(shù)組中,最終在通過 Reflect.defineMetadata 方法進(jìn)行元數(shù)據(jù)的保存。

同樣,我們用一張圖來說明一下 @Get 裝飾器的處理流程:

分析完 @Controller 和 @Get 裝飾器,我們已經(jīng)知道元數(shù)據(jù)是如何進(jìn)行保存的。下面我們來回答 “通過 Reflect API 保存的元數(shù)據(jù)什么時(shí)候使用呢?” 這個(gè)問題。

2.4 元數(shù)據(jù)的使用
要搞清楚通過 Reflect API 保存的元數(shù)據(jù)什么時(shí)候使用,我們就需要來回顧一下前面開發(fā)的 SampleServer 服務(wù)器:

  1. export class SampleServer extends Server { 
  2.   constructor() { 
  3.     super(process.env.NODE_ENV === "development"); 
  4.     this.setupControllers(); 
  5.   } 
  6.  
  7.   private setupControllers(): void { 
  8.     const userController = new UserController(); 
  9.     super.addControllers([userController]); 
  10.   } 
  11.  
  12.   public start(port: number): void { 
  13.     this.app.listen(port, () => { 
  14.       console.log(`⚡️[server]: Server is running at http://localhost:${PORT}`); 
  15.     }); 
  16.   } 
  17.  
  18. const sampleServer = new SampleServer(); 
  19. sampleServer.start(PORT); 

在以上代碼中 SampleServer 類繼承于 OvernightJS 內(nèi)置的 Server 類,對(duì)應(yīng)的 UML 類圖如下所示:

此外,在 SampleServer 類中我們定義了 setupControllers 和 start 方法,分別用于初始化控制器和啟動(dòng)服務(wù)器。我們?cè)谧远x的控制器上使用了 @Controller 和 @Get 裝飾器,因此接下來我們的重點(diǎn)就是分析 setupControllers 方法。該方法的內(nèi)部實(shí)現(xiàn)很簡單,就是手動(dòng)創(chuàng)建控制器實(shí)例,然后調(diào)用父類的 addControllers 方法。

下面我們來分析 addControllers 方法,該方法位于 src/core/lib/Server.ts 文件中,具體實(shí)現(xiàn)如下:

  1. // src/core/lib/Server.ts 
  2. export class Server { 
  3.   public addControllers( 
  4.     controllers: Controller | Controller[], 
  5.     routerLib?: RouterLib, 
  6.     globalMiddleware?: RequestHandler, 
  7.   ): void { 
  8.        controllers = (controllers instanceof Array) ? controllers : [controllers]; 
  9.        // ① 支持動(dòng)態(tài)設(shè)置路由庫 
  10.        const routerLibrary: RouterLib = routerLib || Router;  
  11.        controllers.forEach((controller: Controller) => { 
  12.          if (controller) { 
  13.              // ② 為每個(gè)控制器創(chuàng)建對(duì)應(yīng)的路由對(duì)象 
  14.              const routerAndPath: IRouterAndPath | null = this.getRouter(routerLibrary, controller); 
  15.              // ③ 注冊(cè)路由 
  16.              if (routerAndPath) { 
  17.                   if (globalMiddleware) { 
  18.                       this.app.use(routerAndPath.basePath, globalMiddleware, routerAndPath.router); 
  19.                   } else { 
  20.                       this.app.use(routerAndPath.basePath, routerAndPath.router); 
  21.                   } 
  22.               } 
  23.             } 
  24.         }); 
  25.     } 

addControllers 方法的整個(gè)執(zhí)行過程還是比較清晰,最核心的部分就是 getRouter 方法。在該方法內(nèi)部就會(huì)處理通過裝飾器保存的元數(shù)據(jù)。其實(shí) getRouter 方法內(nèi)部還會(huì)處理其他裝飾器保存的元數(shù)據(jù),簡單起見我們只考慮與 @Controller 和 @Get 裝飾器相關(guān)的處理邏輯。

  1. // src/core/lib/Server.ts 
  2. export class Server { 
  3.  private getRouter(routerLibrary: RouterLib, controller: Controller): IRouterAndPath | null { 
  4.         const prototype: any = Object.getPrototypeOf(controller); 
  5.         const classMetadata: IClassMetadata | undefined = Reflect.getOwnMetadata(classMetadataKey, prototype); 
  6.  
  7.         // 省略部分代碼 
  8.         const { basePath, options, ...}: IClassMetadata = classMetadata; 
  9.  
  10.         // ① 基于配置項(xiàng)創(chuàng)建Router對(duì)象 
  11.         const router: IRouter = routerLibrary(options); 
  12.  
  13.         // ② 為路由對(duì)象添加路徑和請(qǐng)求處理器 
  14.         let members: any = Object.getOwnPropertyNames(controller); 
  15.         members = members.concat(Object.getOwnPropertyNames(prototype)); 
  16.         members.forEach((member: any) => { 
  17.             // ③ 獲取方法中保存的元數(shù)據(jù) 
  18.             const methodMetadata: IMethodMetadata | undefined = Reflect.getOwnMetadata(member, prototype); 
  19.             if (methodMetadata) { 
  20.                 const { httpRoutes, ...}: IMethodMetadata = methodMetadata; 
  21.                 let callBack: (...args: any[]) => any = (...args: any[]): any => { 
  22.                     return controller[member](...args); 
  23.                 }; 
  24.                 // 省略部分代碼 
  25.                 if (httpRoutes) { // httpRoutes數(shù)組中包含了請(qǐng)求的方法和路徑 
  26.                     // ④ 處理控制器類中通過@Get、@Post、@Put或@Delete裝飾器保存的元數(shù)據(jù) 
  27.                     httpRoutes.forEach((route: IHttpRoute) => { 
  28.                         const { httpDecorator, path }: IHttpRoute = route; 
  29.                         // ⑤ 為router對(duì)象設(shè)置對(duì)應(yīng)的路由信息 
  30.                         if (middlewares) { 
  31.                             router[httpDecorator](path, middlewares, callBack); 
  32.                         } else { 
  33.                             router[httpDecorator](path, callBack); 
  34.                         } 
  35.                     }); 
  36.                 } 
  37.             } 
  38.         }); 
  39.         return { basePath, router, }; 
  40.     } 

現(xiàn)在我們已經(jīng)知道 OvernightJS 內(nèi)部如何利用裝飾器來為控制器類配置路由信息,這里阿寶哥用一張圖來總結(jié) OvernightJS 的工作流程:

在 OvernightJS 內(nèi)部除了 @Controller、@Get、@Post、@Delete 等裝飾器之外,還提供了用于注冊(cè)中間件的 @Middleware 裝飾器及用于設(shè)置異常處理中間件的 @ErrorMiddleware 裝飾器。感興趣的小伙伴可以參考一下阿寶哥的學(xué)習(xí)思路,自行閱讀 OvernightJS 項(xiàng)目的源碼。

希望通過這篇文章,可以讓小伙伴們對(duì)裝飾器的應(yīng)用場景有一些更深刻的理解。如果你還意猶未盡的話,可以閱讀阿寶哥之前寫的 了不起的 IoC 與 DI 這篇文章,該文章介紹了如何利用 TypeScript 裝飾器和 reflect-metadata 這個(gè)庫提供的 Reflect API 實(shí)現(xiàn)一個(gè) IoC 容器。

三、參考資源

  • Github - overnight
  • expressjs.com

 

 

責(zé)任編輯:姜華 來源: 全棧修仙之路
相關(guān)推薦

2011-04-13 10:51:58

MATLAB

2021-07-13 07:52:03

SQL面試COUNT(*)

2025-04-15 00:00:00

2019-11-05 10:35:57

SpringBoot調(diào)優(yōu)Java

2025-03-28 03:20:00

MySQL數(shù)據(jù)庫搜索

2023-03-01 23:59:23

Java開發(fā)

2024-06-12 12:28:23

2024-11-25 18:00:00

C#代碼編程

2021-01-04 15:11:57

開發(fā) IDEA代碼

2024-11-27 09:46:34

2011-02-25 08:39:11

QFabric數(shù)據(jù)中心Juniper

2013-01-07 09:34:43

CodeLoveBAT

2025-01-17 09:23:31

2016-01-19 17:03:59

數(shù)據(jù)中心網(wǎng)絡(luò)華為

2019-03-25 08:05:35

Elasticsear優(yōu)化集群

2011-09-27 13:25:05

Web

2022-09-02 08:21:24

idea插件

2020-12-31 07:59:11

SaaS軟件即服務(wù)軟件

2011-10-18 10:30:11

筆記本評(píng)測

2016-05-11 09:18:21

AWS云數(shù)據(jù)倉庫Redshift
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)