ts裝飾器基本的用法,並使用裝飾器完成@Controller等裝飾器,簡化路由開發


TS

TypeScript 是一種由微軟開發的自由和開源的編程語言。它是 JavaScript 的一個超集,而且本質上向這個語言添加了可選的靜態類	型和基於類的面向對象編程。
TypeScript 擴展了 JavaScript 的句法,所以任何現有的 JavaScript 程序可以不加改變的在 TypeScript 下工作。	TypeScript 是為大型應用之開發而設計,而編譯時它產生 JavaScript 以確保兼容性。
這段是我在網上扒下來的

為什么要用TS對express進行封裝呢?

主要是為了用ts里面的裝飾器,雖然裝飾器已經是es7里面的一個提案了,但是在js里面
使用的話還需要用babel進行配置(太麻煩了,圖省事所以用Ts)

我們先來了解一下什么是裝飾器

裝飾器(Decorator) 僅提供定義劫持,能夠對類及其方法、方法入參、屬性的定義
並沒有提供任何附加元數據的功能。
主要是為了提供附加的功能

那么TS里面的裝飾器如何使用呢

//需要在tsconfig開啟裝飾器
{
	"compilerOptions": {
    	"experimentalDecorators": true
	}
}

二話不說npm一把梭
npm init -y //使用默認配置
npm i typescript -S //安裝ts包
tsc --init //初始化ts配置
mkdir src //創建src目錄
mkdir decorators //創建裝飾器目錄
touch common.ts //創建common 用於辨析而裝飾器
touch index.ts //創建index文件
到此為止已經搭建了項目的基本架構

接下來我們介紹一下TS里面有那些類型的裝飾器

類裝飾器

應用於類的構造函數
參數:target (類)
寫法:function classDecorators(target:any){
	//我們在這個地方來對象進行操作
    //比我我們給類里面的原型加一點東西
    target.prototype.usernmae='沉默'
}

屬性裝飾器

應用於屬性上面的裝飾器
參數:targte (類),name(當前屬性的名字)
寫法:
function attrDecorators(target:any,name:string){
    target[name]=150 //我們給當前屬性賦了一個值
}

方法裝飾器

應用於方法上面的裝飾器
參數:targte (類),name(當前方法的名字),descriptor (當前方法的描述對象)
寫法:
function funcDecorators(target:any,name:string,descriptor:PropertyDescriptor){
    //我們在這個裝飾器里面來寫一些,在每次執行的時候打印一個時間
    let origin=descriptor.value;//這個就是當前方法的內容,我們給他保存起來
    descriptor.value=function(){
        console.log(new Date())
        //執行我們保存的那個方法 arguments是傳進來的那些參數
        return origin.apply(this,arguments)
    }
    return descriptor
}

方法參數的裝飾器

應用於方法參數上面的裝飾器
參數:target(類),name(當前參數方法的名稱),index(按當前參數的在形參列表里面的索引值)
function paramDecorators(target:any,name:string,index:number){
	//這個裝飾器也沒有上面例子好舉了
}

這是我們講的ts的裝飾器,我們一起加在一個類上面,來看看他的執行順序是上面樣的


我們可以看到先走的是屬性的裝飾器,之后是參數在是方法然后是類

我看別人的裝飾器里面都能傳值,為什么我看你這個不能呢!

不要着急,馬上就會講到,TS里面還有一個叫裝飾器工廠的裝飾器就是用來完成這個工作的。
裝飾器工廠的定義方法就是返回的是一個方法
來看例子
function classDesc(path:string){
  //這就是一個裝飾器工廠,上面的path就是你可以傳進來的值
  console.log(path,'class')
  return (target:any)=>{
      console.log(typeof target,'class')
  }
}
其他類型的裝飾器的寫法也都類似與這樣,只不過是參數不一樣

接下來我們講講js里面的元數據

元數據即為描述數據的數據,也有個稱呼叫
注解(Annotation) 僅提供附加元數據支持,並不能實現任何操作。需要另外的 Scanner 根據元數據執行相應操作。
但是我一聽到注解就像Java里面的東西
總而言之,裝飾器和注解是不一樣的,我上面有講過裝飾器的概念,主要是為了提供附加功能,注解不提供這些,它只提供元數據的支持,不能操作任何東西,所以只能根據元數據來實現具體的操作。

知道元數據之后我們就可以來封裝標題上面所講的裝飾器了,封裝那些需要用到

我們最終會是實現這些裝飾器
Controller Get Post Body Query 等
之前我們以及創建了common.ts文件,那么就在這里先創建第一個裝飾器(Controller)
const PATH_METADETA='path';
const PATH_METADETA = "path";
const PARAMS_METADETA = "params";
function Controller(path:string):ClassDecorator{
	//創建裝飾器工廠,可以用來接收參數,上面以及講過了
  return (target)=>{
  	/*
      給當前類添加一個元數據
      Reflect.defineMetadata用來添加元數據,總共有四個值第一個是key,唯一的,
      第二個是值,第三個是當前對象,第四個是方法名,但我們當前只給類設置元數據,就不需要
      填方法
      為什么要設置元數據呢,裝飾器本來就是用來擴展方法的,但是他是用來對本方法進行擴展的,
      在此裝飾器里面我們拿不到,具體的數據,所以我們用元數據去設置具體的數據,在其他地方進行
      通用的注冊使用,這個裝飾器的作用就是用設置元數據的(我也不知道這樣講能不能明白:))
      */
  	Reflect.defineMetadata(PATH_METADETA, path, target);//當前裝飾器傳過來
      //的數據給設置進入了類的元數據里面了,日常的時候這個類並不影響我們的使用
  }
}
//我們需要編寫Post Get 這些的方法,本質上這些方法除了Post,Get這些不一樣,傳過來的東西都
//是一樣的
function createdMethod(methods: string) {
  return (path: string): MethodDecorator => {
    return (target: any, key: string | symbol, descriptor: any) => {
      //給當前方法設置了一個
      Reflect.defineMetadata(PATH_METADETA, path, target, key);
      //添加方法元數據,描述當前方法為什么方法
      Reflect.defineMetadata(METHOD_METADETA, methods, target, key);
    };
  };
}
目前我們先寫這兩個裝飾器,之后在寫params的裝飾器

裝飾器雖然是寫好了

但是我們在那里去用呢,不會這么簡單吧。
當然不會那么簡單,我們現在還需要一個注冊的地方,我們需要讓express拿到裝飾器裝飾上去的
元數據。

我們在decorators里面在建一個文件 叫	register.ts 用來注冊我們裝飾的類
下面是代碼
import {Router} from 'express';
function register(app:any,controller:Array<any>){
	const router=Router();
 	controller.forEach((v:any)=>{
         //這個v就是類
         //獲取當前類key為path的元數據 也就是之前我們裝飾器設置的值
         let classPath=Reflect.getMetadata('path',v);
         //遍歷當前屬性,查詢當前類屬性上面有哪些元數據
         for(const key in new v()){
         	//這個getMetadata里面的key是當前遍歷的方法的名稱
             //也就拿到當前方法設置的元數據
             let attrMethod = Reflect.getMetadata('method', new v(), key);
             let attrPath = Reflect.getMetadata('path', new v(), key);
             //我們在定義一個回調方法,這個就是express的回調方法
             let fn:any=(req,res,next)=>{
             	//我們先把我們當前的方法給執行一下,在傳兩個參數(這個方法就是@Post@Get這個中下面的方法)
               let result=new v()[key].apply(new v(),[req,res]);
               if (result instanceof Promise) {
               	result.then(value => {
               		!res.headersSent && res.send(value);
               	}).catch(err => {
               		next(err)
               	})
               } else if (result !== undefined) {
               		!res.headersSent && res.send(result);
               }
                 
             }
             //組裝一下路由的路徑
             let routerParams = [classPath + attrPath]
             //在添加回調
             routerParams.push(fn)
             //去調用router方法
             router[attrMethod].apply(router, routerParams)
         }
         //用來解決body
         app.use(bodyParser.json())
         app.use(bodyParser.urlencoded({ extended: false }))
         app.use(router)//使用路由
 	})
}
在index.ts 里面啟動express並注冊,我們的裝飾器應該就能和router聯動起來了
  register(app,[UserController]);

  var server = app.listen(3122, function () {
      var host = server.address().address;
      var port = server.address().port;
      console.log('Example app listening at http://%s:%s', host, port);
  })
自此也就算是寫完了!我也是研究了一下午,看了github上面的一些主流的框架的源碼,剝離出來的簡單的版本

我也是一個沒干多久小菜鳥,希望這個篇文]章對你們有所幫助
我自己寫的那個項目在[github](https://github.com/mrzhouxl/express-decorator)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM